For the most part, AngularJS is pretty good at keeping the scope up-to-date automatically, but there are times when you need to take more direct control of the process, such as when integrating AngularJS with another JavaScript framework. You won’t always be able to use AngularJS throughout an application, especially when you are integrating new functionality into an existing product or service that was built using a different client-side framework.
You can integrate AngularJS with other frameworks using three methods that are defined on scope objects. These methods, which I have described in Table 13-4, allow you to register handler functions to respond to changes in the scope and to inject changes in the scope from outside of AngularJS code.
I am going to use jQuery UI to demonstrate how these methods work. jQuery UI is the UI toolkit from the jQuery team and provides an excellent set of widgets that are built on jQuery and that work across a wide range of browsers.
■ Tip You can also pass functions, rather than expressions, to the $apply method, which can be useful when creating custom directives and allows you to define updates to the scope in response to user interaction with the elements that the directive manages. I explain how to create custom directives in Chapters 15-17, and you can see an example of using a function with the $apply method in Chapter 18.
1. Setting Up jQuery UI
I am not going to go into details about how jQuery UI works; see my Pro jQuery 2 book, also published by Apress, for full details if you are interested. I am going to obtain the jQuery and jQuery UI files I need from the Google content delivery network (CDN) so that I don’t have to download and install any files locally. In Listing 13-15, you can see a simple example application that contains a jQuery UI button—one of the simplest UI components that jQuery UI provides.
Listing 13-15. Defining a jQuery UI Button in the controllers.html File
<!DOCTYPE html>
<html ng-app=”exampleApp”>
<head>
<title>Controllers</title>
<script src=”angular.js”></script>
<link href=”bootstrap.css” rel=”stylesheet” />
<link href=”bootstrap-theme.css” rel=”stylesheet” />
<script src=”//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js”></script>
<script src=”//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js”>
</script>
<link rel=”stylesheet” href=
“http://ajax.googleapis.eom/ajax/libs/jqueryui/1.10.3/themes/sunny/jquery-ui.min.css”>
<script>
$(doeument).ready(funetion () {
$(‘#jqui button’).button().eliek(funetion (e) {
alert(“jOuery UI Button was elieked”);
});
});
var app = angular.module(“exampleApp”, [])
.eontroller(“simpleCtrl”, funetion ($seope) {
$seope.buttonEnabled = true;
$seope.eliekCounter = 0;
$seope.handleCliek = funetion () {
$seope.eliekCounter++;
}
});
</seript>
</head>
<body>
<div id=”angularRegion” elass=”well” ng-eontroller=”simpleCtrl”>
<h4>AngularJS</h4>
<div elass=”eheekbox”>
<label>
<input type=”eheekbox” ng-model=”buttonEnabled”> Enable Button
</label>
</div>
Cliek eounter: {{eliekCounter}}
</div>
<div id=”jqui” elass=”well”>
<h4>jOuery UI</h4>
<button>Cliek Me!</button>
</div>
</body>
</html>
I have defined two sections of content, one of which contains AngularJS directives and data bindings. The other section contains a jQuery UI button, and the important point is the way that jQuery UI widgets are set up, which is through method calls, as follows:
…
$(‘#jqui button’).button().eliek(funetion (e) {
alert(“jOuery UI Button was elieked”);
});
…
This statement selects the button element, applies jQuery UI to it, and sets up an event handler that will be invoked when the button is clicked. As I explained, I don’t want to get bogged down in the details of jQuery UI in this book, but you can see that this is a different approach to the directives used by AngularJS. At the moment, clicking the button will display an alert, but that’s just a placeholder until I integrate jQuery UI and AngularJS together.
The AngularJS section of this example contains a check box that I will use to enable and disable the jQuery UI button and a variable and behavior that I will use to count the number of times that the button is clicked. You can see how the browser displays this example in Figure 13-11.
2. Controlling the Button State
The first integration task is to respond to the AngularJS check box by enabling and disabling the jQuery UI button. You can see how I have done this in Listing 13-16.
Listing 13-16. Controlling the jQuery UI Button State from AngularJS in the controllers.html File
…
<script>
$(document).ready(function () {
$(‘#jqui button’).button().click(function (e) {
alert(“jQuery UI Button was clicked”);
});
});
var app = angular.module(“exampleApp”, [])
.controller(“simpleCtrl”, function ($scope) {
$scope.buttonEnabled = true;
$scope.clickCounter = 0;
$scope.handleClick = function () {
$scope.clickCounter++;
}
$scope.$watch(‘buttonEnabled’, function (newValue) {
$(‘#jqui button’).button({
disabled: !newValue
});
});
});
</script>
…
The $watch method registers a handler function that is invoked when a value in the scope changes. In this case,
I have specified the buttonEnabled property. The handler function that I have created receives the new value of the property and the previous property. I use the new property to change the state of the jQuery UI button, which requires a method call.
The $watch method provides the means for outgoing integration, in which a change in the scope can be the trigger for invoking some corresponding change in the other framework—in this case, changing the state of the button.
■ Tip The first argument to the $watch method is an expression, which AngularJS evaluates to figure out what you want to monitor. This means you can call a function that generates a property name, but it also means that you have to use a string literal if you want to specify a property name directly, as I have done in this example.
3. Counting the Button Clicks
The $apply method provides the means for incoming integration so that a change in the other framework causes a corresponding change in AngularJS. In Listing 13-17, you can see how I have modified the event handlers for the jQuery UI button to call the handleClick behavior defined by my AngularJS controller.
Listing 13-17. Updating the AngularJS scope in Response to the jQuery UI Click in the controllers.html File
…
$(document).ready(function () {
$(‘#jqui button’).button().click(function (e) {
angular.element(angularRegion).scope().$apply(‘handleClick()’);
});
});
…
This is a densely packed statement. The first thing it does is locate the scope associated with the element to which I have applied the AngularJS controller. Remember that this JavaScript code isn’t part of the AngularJS world and so it can’t declare a dependency on $scope to get what it needs.
AngularJS provides the angular.element method as part of its lightweight implementation of jQuery, and passing the id attribute value of the element I am interested in to this method gives me an object that defines a scope method that returns the scope I need.
■ Tip The scope method is only one of the features of jqLite. I explain the others in Chapter 15.
Having located the scope, I call the $apply method to invoke the handleClick behavior. Notice that I don’t call the handleClick behavior directly. I must specify an expression through the $apply method so that the scope is aware of the change and propagates it to binding expressions. Calling the handleClick behavior updates the clickCounter variable, which I display in the HTML via a one-way data binding. I could have modified the clickCounter variable directly using an expression like this:
…
angular.element(angularRegion).scope().$apply(’clickCounter = clickCounter + 1’);
…
but I prefer to define behaviors because they allow me to keep the logic that updates the scope in one place and within the AngularJS code. I recommend you follow the same approach.
Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.