• No results found

DOM manipulations are for Directives

In document Software Developer Journal (Page 89-92)

Listing 11.1. HTML using AngularJS to handle form based application

<form name=”myForm”>

<input type=”text” ng-model=”user.name”>

<input type=”email” ng-model=”user.email”>

<button class=”updateButton” ng-click=”updateUser()”>Update</update>

</form>

Listing 11.2. JS for using AngularJS to handle form based application

// Inside a controller var fetchData = function() {

$http.get(‘/api/user’).success(function(user) { $scope.user = user;

});

};

$scope.updateUser = function() {

$http.post(‘/api/user’, $scope.user);

};

You can immediately see that in the AngularJS code, we don’t have to write any code to transfer the data from the UI to the code and back from the code the UI. We leverage AngularJS’s data-binding and thus significantly reduce the amount of code we write (and thus the possibility of errors as well!).

DOM manipulations are for Directives

SO where do these jQuery DOM manipulations belong, if they don’t belong in the controller? What about some more complex examples, like needing an accordion or datepicker, which are available in say jQuery-UI. Wouldn’t the controller be where I mark the correct the input fields or DIV elements using jQuery?

The association you want to start making in Angular is to use Directives whenever you need to transform or manipulate the DOM. Need to create an input datepicker? Think directive. Need to create a reusable component to display thumbnails of images? Think Directive!

Directives are AngularJS’ way of encapsulating both view and corresponding logic into a reusable component. For example, let us take how you would traditionally use a jQuery UI datepicker:

$(‘#inputDate’).datepicker();

Then, we would have to get and set the date as follows:

$(‘#inputDate’).datepicker(‘getDate’);

$(‘#inputDate’).datepicker(‘setDate’, ‘05/23/1975’);

Now consider adding other options on a case by case basis. You might want to know when the user selects a date. or you might want to set a different date format instead of the default MM/DD/YYYY. While you can do all of this normally, there are a few pain-points. Namely,

• This is not declarative. Someone would look at the HTML and never realize that the input field magically becomes a datepicker at some point. You would have to dig through the code to find out who is doing what

figure out how to do common things.

• Anyone looking to reuse the component would have to end up copy pasting this code, and rewrite callback functions and instantiations wherever they need it. Or copy paste a lot of code.

Now consider the alternative, where jQuery UI datepicker is wrapped as a reusable component, exposing just the most commonly used APIs in a declarative manner. For example, in an ideal world, I would want to do something like:

<input type=”text” jqui-datepicker ng-model=”startDate” date-format=”dd/mm/yyyy” on-select=”dateSelected(date)”>

Anyone looking at the HTML can immediately understand that the value of the datepicker is available in the model variable called “startDate”, and that when the date is selected, a function called dateSelected is called.

Listing 12 demonstrates how we would write such a directive. This directive needs to take care of two things:

• Getting the data from the jQuery UI datepicker, and informing AngularJS about its change

• Telling jQuery UI about any changes that happen inside of AngularJS

Also note the use of scope.$apply(). This is to let AngularJS know that the model has changed outside of AngularJS’s control, and it needs to update all the views to reflect this new change. In this case, it is the user selecting a date in the jQuery UI datepicker widget.

Listing 12. A Simple Jquery UI Datepicker Directive

angular.module(‘fundoo.directives’, []) .directive(‘datepicker’, function() { return {

// Enforce the angularJS default of restricting the directive to // attributes only

restrict: ‘A’,

// Always use along with an ng-model require: ‘ngModel’,

// This method needs to be defined and passed in from the // passed in to the directive from the view controller scope: {

select: ‘&’ // Bind the select function we refer to the right scope },

link: function(scope, element, attrs, ngModelCtrl) { var optionsObj = {};

// Use user provided date format, or default

optionsObj.dateFormat = attrs.dateFormat || ‘mm/dd/yy’;

optionsObj.onSelect = function(dateTxt, picker) { scope.$apply(function() {

// Update AngularJS model on jQuery UI Datepicker date selection ngModelCtrl.$setViewValue(dateTxt);

if (scope.select) {

scope.select({date: dateTxt});

} });

};

// Notify jQuery UI datepicker of changes in AngularJS model ngModel.$render = function() {

// Use the AngularJS internal ‘binding-specific’ variable element.datepicker(‘setDate’, ngModelCtrl.$viewValue || ‘’);

};

element.datepicker(optionsObj);

} };

});

This is one type of directive, where we care about input from the user. On the other hand, sometimes, we might want to just get data into our widget and display it.

For example, if we wanted a custom component that we use to display a photo along with its comments, likes and other metadata in a grid, we could create a component that we end up using as follows:

<div my-photoview photo-meta=”photoObj”></div>

The JS code for the myPhotoview widget would look something like Listing 13.

Listing 13. A photo display widget

angular.module(‘fundoo.directives’).directive(‘myPhotoview’, function() { return {

restrict: ‘A’, scope: {

photoMeta: ‘=’, },

template: ‘<div class=”photo-widget”>’ +

‘<img ng-src=”{{photoMeta.url}}”/>’ +

‘<span class=”caption”>{{photoMeta.caption}}</span>’ + ‘</div>’,

link: function($scope, $element, $attrs) {

// More specific rendering logic, watches, etc can go here }

};

});

Here, photoObj is a javascript object that contains the URL of the photo, the caption, the comments information and the number of likes. The directive could encapsulate all the logic of how this is rendered, and other additional functionality like liking the photo, commenting on the photo, etc. It might even decide to conditionally include other templates, or use jQuery to manipulate certain parts of its template.

The interesting things to note here are

• The naming convention: When we declare our directive in the JS code, we defined it as ‘myPhotoview’.

But when we use it in the HTML, we need to use it as my-photoview. The camelcase from the JS gets translated to dash separated words in the HTML. This is true for the directive as well as all the attributes defined on it.

• The scope definition: The scope defines something called ‘photoMeta’, with its value as ‘=’. This means that when the directive is used, we can pass in any javascript object to it using the attribute photo-meta in the HTML, and the value of the javascript object will be available within the directive as $scope.

photoMeta. In the case of Listing 13, we can access the contents of photoObj as $scope.photoMeta and display it.

• Data-binding: The best part about defining the photoMeta in the scope as ‘=’ is that it tells AngularJS that the object needs to be kept upto date inside the directive. That is, if photoObj changes in the parent controller, then the latest value must be made available to the directive. No extra code needed!

• Link function: The link function is the place to put additional logic. For example, while the caption and the image itself would change automatically if photoObj ended up changing in Listing 13, if we wanted to do some additional data manipulation or DOM manipulation, the link function is where that code would go.

The main take-away from both these examples is to encapsulate all this DOM modifying behavior within Directives.

When to use AngularJS Services (or factories, or

In document Software Developer Journal (Page 89-92)