Documentation

jQWidgets Custom AngularJS Directives

AngularJS

AngularJS is an open-source JavaScript framework, maintained by Google, whose goal is to augment web-based applications with model–view–controller (MVC) capability, in order to make both development and testing easier.

In this tutorial you will learn the basics of using jQWidgets with AngularJS. We will showcase several examples, which demonstrate how the widgets can benefit from Angular's directives and two-way data binding approach.

Get AngularJS

You can download AngularJS from http://angularjs.org. It is recommended, however, that you include a reference to the Google CDN-hosted file instead. Here is a link to the current (at the time of writing) version of the framework:
https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js

Example #1: jqxInput with Two-way Data Binding

Our first example shows two jqxInput instances which are used to change the text of a welcome message. You can check out the example angularjs-jquery-input.htm. Here are the key points of integrating jqxInput with AngularJS:

1. The ng-app directive

In AngularJS, directives are custom HTML attributes enabling specific Angular functionalities. To define an AngularJS application, add the ng-app directive to an element. If you add it to html, Angular will be available in the whole page:

<html xmlns="http://www.w3.org/1999/xhtml" ng-app="demoApp">

We define the Angular app in our custom script as follows:

var demoApp = angular.module("demoApp", []);

2. The Controller

The controller is the part of an Angular application which allows the view to update the model and vice versa (two-way data binding). Here is the controller of this example:

demoApp.controller("labelController", function ($scope) {
$scope.firstName = "Matt";
$scope.lastName = "Smith";
});

The $scope of the controller holds all variables and functions of the model. In our case, we have firstName and lastName.

The controlled part of the page (this is usually the body) has to have the ng-controller directive pointing to the controller:

<body ng-controller="labelController">

3. The View

Here is the view (HTML) of our example:

<body ng-controller="labelController">
<div>
<p>
First name:
<input id="firstName" value="{{firstName}}" ng-model="firstName" /></p>
<p>
Last name:
<input id="lastName" value="{{lastName}}" ng-model="lastName" /></p>
<h2>
Welcome, {{firstName}} {{lastName}}!</h2>
</div>
</body>

The directive ng-model denotes that when you type in the inputs, the $scope variables firstName and lastName are updated with the respective input's value. The double curly brace notation {{ }} to bind expressions to elements is built-in Angular markup. They are replaced with the value of the $scope variable inside. In the example, they are used to sync the displayed value of the inputs with the value of firstName and lastName respectively.

4. jqxInput Initialization

We initialize the jqxInput instances, as usual, on $(document).ready():

$(document).ready(function () {
$("#firstName, #lastName").jqxInput({ theme: "darkblue", width: 200, height: 25 });
});
An alternative way is to define a custom Angular directive, as shown in the tutorial jQWidgets AngularJS Directives.


Example #2: Loading jqxDataTable from an Angular-bound Table

In this and the next two examples, we will discuss only the points that were not covered in the overview of Example #1.

The second example shows a data table that has been loaded from an HTML table, which is bound to an array in the controller's scope. You can view the example angularjs-jquery-datatable.htm. And here is the code of the AngularJS controller:

demoApp.controller("peopleController", function ($scope) {
$scope.people = [{
id: 1,
name: "Pedro",
age: 13
}, {
id: 2,
name: "Clara",
age: 22
}, {
id: 3,
name: "John",
age: 33
}, {
id: 4,
name: "Pier",
age: 27
}];
});

There is only one $scope variable - people, which is an array of objects.

The essential part of the example is the view, which contains a table bound to the people array through the ng-repeat directive, which creates a row for each item (which we call person in the directive) in the array:

<body ng-controller="peopleController">
<table id="dataTable">
<thead>
<tr>
<th>
Id
</th>
<th>
Name
</th>
<th>
Age
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="person in people">
<td>
{{person.id}}
</td>
<td>
{{person.name}}
</td>
<td>
{{person.age}}
</td>
</tr>
</tbody>
</table>
</body>

The data table is finally initialized in $(document).ready() from the now-populated HTML table:

$(document).ready(function () {
$("#dataTable").jqxDataTable(
{
theme: "darkblue",
altrows: true,
sortable: true,
columns: [
{ text: 'Id', dataField: 'Id', width: 50 },
{ text: 'Name', dataField: 'Name', width: 100 },
{ text: 'Age', dataField: 'Age', width: 50 }
]
});
});
angularjs jquery datatable

Example #3: Model Manipulation with jqxScrollBar

Example #3 shows how to manipulate a variable from the AngularJS model through jqxScrollBar. The example can be found angularjs-jquery-scrollbar.htm. The Angular controller of this example has only one variable - value:

demoApp.controller("scrollbarController", function ($scope) {
$scope.value = 500;
});

The view updates whenever value is updated:

<body ng-controller="scrollbarController">
<div>
<div id='jqxScrollBar'>
</div>
<h2>
Value: <span>{{value}}</span></h2>
</div>
</body>

The key here is the code in $(document).ready():

$(document).ready(function () {
var scope = angular.element($("body")).scope();
$("#jqxScrollBar").jqxScrollBar({ theme: "darkblue", width: 250, height: 18, value: scope.value });
$("#jqxScrollBar").bind('valuechanged', function (event) {
scope.$apply(function () {
scope.value = parseInt(event.currentValue);
});
});
});

First, we get the scope of our Angular application. Then we initialize the scrollbar and bind to its valuechanged event - each time the scrollbar's value is changed, the scope value is updated accordingly via the scope.$apply method. This, in turn, updates the view.

AngularJS jQuery ScrollBar

Example #4: Add/Remove Items to a jqxDropDownList

In the final example of this tutorial, we will show you how to add and remove items to a jqxDropDownList instance. The dropdown list is bound to an array of items, which is part of the scope (model). There is an input in which the user can enter a new item and two buttons - Add and Remove selected. Click angularjs-jquery-dropdownlist.htm to view the example now.

AngularJS JQuery Dropdownlist

The input is bound to the scope variable newItem. When you click Add, the scope function addItem is invoked thanks to the ng-click directive and the item is added to the items array. The dropdown list is updated, too. Clicking Remove selected removes the selected item from items and updates the dropdown list (this is also done through ng-click). Here is the releavant part of the code:

    <script type="text/javascript">
var demoApp = angular.module("demoApp", []);
demoApp.controller("itemsController", function ($scope) {
$scope.items = ["Bread", "Milk", "Sugar", "Salt", "Honey", "Orange juice", "Mellon", "Potatoes"];
$scope.newItem;
$scope.addItem = function () {
if ($scope.newItem.length > 0) {
$scope.items.push($scope.newItem);
$("#jqxDropDownList").jqxDropDownList({ source: $scope.items });
$scope.newItem = "";
};
};
$scope.removeItem = function () {
var itemToRemove = $("#jqxDropDownList").jqxDropDownList("val");
var i = $scope.items.indexOf(itemToRemove);
if (i != -1) {
$scope.items.splice(i, 1);
$("#jqxDropDownList").jqxDropDownList({ source: $scope.items, selectedIndex: -1 });
};
};
});
$(document).ready(function () {
var scope = angular.element($("body")).scope();
$("#jqxDropDownList").jqxDropDownList({ theme: "darkblue", source: scope.items, autoDropDownHeight: true, height: 25, width: 200 });
$("#newItemInput").jqxInput({ theme: "darkblue", height: 22 });
$("#addButton, #removeButton").jqxButton({ theme: "darkblue" });
});
</script>
</head>
<body ng-controller="itemsController">
<div id="jqxDropDownList">
</div>
<div style="margin-top: 10px;">
New item:
<input id="newItemInput" ng-model="newItem" value="{{newItem}}" />
<button id="addButton" ng-click="addItem()">
Add</button>
<button id="removeButton" ng-click="removeItem()">
Remove selected</button>
</div>
</body>

That concludes our overview of some basic AngularJS data-binding examples featuring jQWidgets.

Below, we will show you how to use custom AngularJS directives to initialize and update jQWidgets controls.

Directives are markers on a DOM element (such as an attribute, element name, or CSS class) that tell AngularJS's HTML compiler to attach a specified behavior to that DOM element or even transform the DOM element and its children. Angular comes with a set of these directives built-in, like ngBind, ngModel, and ngView. Derective names can be written in three equally correct ways, e.g. ngModel, ng-model and data-ng-model. Much like you create controllers and services, you can create your own directives for Angular to use. When Angular bootstraps your application, the HTML compiler traverses the DOM matching directives against the DOM elements. You can learn more about directives in Angular's Developer Guide: http://docs.angularjs.org/guide/directive.

In our guide we will showcase several different approaches of using custom directives to initialize widgets and update their properties.


Example #1: Initialize and Update jqxScrollBar through a Custom "Settings" Directive

The first example we will discuss features jqxScrollBar. To try it out, please visit: angularjs-jquery-scrollbar.htm. Here are the key points of the example:

1. The Angular App References the Directive

The custom directive, which we have placed in an external file, has to be referenced in the Angular app definition like so:

var demoApp = angular.module("demoApp", ["ngJqxsettings"]);

A reference to the file ngjqxsettings.js has to be added to the page, too:

<script type="text/javascript" src="ngjqxsettings.js"></script>

2. Controller

We have a controller with three scope variables - theme, width and value - which we will use to set the properties of the jqxScrollBar.

demoApp.controller('initController', function ($scope) {
$scope.theme = "energyblue";
$scope.width = 500;
$scope.value = 333;
});

3. View

The view consists of the div used for initialization of the jqxScrollbar and several controls for changing its properties. These are bound to the model via the standard Angular directive ng-model.

<body ng-controller="initController">
<div id="jqxScrollBar" ng-jqwidgets="jqxScrollBar" ng-jqxsettings="{ theme: theme, width: width, height: 18, value: value}">
</div>
<p>
Change the scrollbar's width:<br />
<input type="text" value="{{width}}" ng-model="width" /></p>
<p>
Change the scrollbar's value:<br />
<input type="text" value="{{value}}" ng-model="value" /></p>
<p>
Change the scrollbar's theme:<br />
<select ng-model="theme">
<option>arctic</option>
<option>energyblue</option>
<option>darkblue</option>
<option>orange</option>
<option>ui-le-frog</option>
</select></p>
</body>

The essential part here is our custom directive, ng-jqxsettings, which is applied in the form of an attribute to the jqxScrollBar div. The value of the attribute is an object with the settings we would like to initialize the widget with. Some of these are set explicitly (such as height: 18) and some refer to variables in the Angular controller (value: value). Our directive looks to the custom attribute ng-jqwidgets to determine which particular widget should be initialized with the given settings. Note that to initialize another type of widget, you will also need a reference to its script in the page.

4. The Custom Directive

Now lets go "behind the scenes" and see how the ng-jqxsettings directive actually works. As already mentioned, the directive is found in the file ngjqxsettings.js. Here is its source code:

angular.module("ngJqxsettings", []).directive('ngJqxsettings', function ($interval, dateFilter) {
function link(scope, element, attrs) {
var isInitialized = false;
var widget = attrs.ngJqwidgets; // (1)
var propertyToScopeArray = propertyToScope(attrs.ngJqxsettings); // (2)
for (var p = 0; p < propertyToScopeArray.length; p++) {
var property = propertyToScopeArray[p].scopeVar;
eval("var " + property + " = scope." + property + ";"); // (3)
// watches for changes in the $scope variables and updates the widget accordingly (4)
scope.$watch(property, function (newValue, oldValue) {
if (isInitialized == true && newValue != oldValue) {
var setting = this.exp;
eval(setting + " = scope." + setting);
var updatedProperty;
for (var i = 0; i < propertyToScopeArray.length; i++) {
if (setting == propertyToScopeArray[i].scopeVar) {
updatedProperty = eval("({ " + propertyToScopeArray[i].jqwidgetsProperty + ": " + setting + " })");
break;
};
};
if (updatedProperty != undefined) {
$(element[0])[widget](updatedProperty);
};
};
});
};
// initializes the widget
var settings = eval("(" + attrs.ngJqxsettings + ")"); // (5)
$(document).ready(function () {
$(element[0])[widget](settings); // (6)
isInitialized = true;
});
};
return {
link: link
};
});
// extracts the key-value pairs from the settings string
var propertyToScope = function (settingsString) {
var newString = settingsString.slice(1, settingsString.length - 1);
newString = newString.trim();
var keyValueArray = newString.split(",");
var keyValueObjects = new Array();
for (var i = 0; i < keyValueArray.length; i++) {
var tempString = keyValueArray[i].trim();
var tempArray = tempString.split(":");
var scopeVar = tempArray[1].trim();
if (isNaN(scopeVar) == true && scopeVar != "true" && scopeVar != "false" && scopeVar[0] != "\"" && scopeVar[0] != "\'") {
keyValueObjects.push({ jqwidgetsProperty: tempArray[0].trim(), scopeVar: tempArray[1].trim() });
};
};
return keyValueObjects;
};

Each Angular directive should return an object with a link callback function, implementing the required functionality. Other properties can be added to the returned object, too, but we will focus solely on link.

First, we get the name of the widget to initialize from the ng-jqwidgets attribute (1). The next step is to convert the settings string that was passed to the directive into an array of key-value pairs we can better work with. This is done by the function propertyToScope (2). The call of eval that follows (3) is needed so that we have local variables with the values of the ones from the scope. This means that when we eval the settings string (5) to pass it to the widget as an object, theme, width and value will not be undefined but will be equal to their scope counterparts.

We bind to scope.$watch to update the properties of the widget whenever the values of their respective scope variables change (4). For example, if we change the theme from the select to "orange", the scope.$watch callback function will update the scrollbar's theme accordingly.

In $(document).ready(), the widget is initialized with the settings provided in the directive (6). Note that the ng-jqxsettings directive is not widget-specific and can be used to initialize all types of jQWidgets controls. In the next example, a jqxButton is initialized with it.

angularjs scrollbar

Example #2: Initialize and Reload jqxGrid through a Custom Directive

In the second example (angularjs-jquery-grid.htm), we will showcase a jqxGrid-specific custom directive which allows initialization and reload of the widget. The button which reloads the grid is initialized with the ng-settings directive, which we discussed in Example #1. That is why we will only make an overview of the custom grid directive. We have called it ng-jqxgrid and it is applied as an attribute to the grid initialization div in the View:

<body ng-controller="initController">
<div id="jqxGrid" ng-jqxgrid="{ theme: theme, source: dataAdapter, width: gridWidth, disabled: disabled, autoheight: true,
editable: true,
selectionmode: 'singlecell',
columns: columns }">
</div>
<br />
<div id="jqxButton" ng-jqwidgets="jqxButton" ng-jqxsettings="{ theme: theme, width: buttonWidth, height: 20 }"
ng-click="reloadGrid()">
<img style='position: relative; margin-top: 2px;' src="../../images/refresh.png" /><span
style='margin-left: 4px; position: relative; top: -3px;'>Reload grid</span></div>
</body>

We pass an object with the desired grid settings to the directive. Some properties have static values (e.g. selectionmode) and some (e.g. columns) refer to variables from the Angular scope. And here is the Angular controller of the page:

demoApp.controller('initController', function ($scope) {
$scope.theme = "energyblue";
$scope.data = generatedata(12);
$scope.source =
{
localdata: $scope.data,
datafields:
[
{ name: 'productname', type: 'string' },
{ name: 'quantity', type: 'number' },
{ name: 'price', type: 'number' }
],
datatype: "array"
};
$scope.dataAdapter = new $.jqx.dataAdapter($scope.source);
$scope.gridWidth = 500;
$scope.disabled = false;
$scope.columns = [
{ text: 'Product Name', dataField: 'productname', width: 200 },
{ text: 'Quantity', dataField: 'quantity', width: 150, cellsformat: 'c2', cellsalign: 'right' },
{ text: 'Price', dataField: 'price', width: 150, cellsformat: 'c2', cellsalign: 'right' }
];
$scope.buttonWidth = 100;
$scope.reloadGrid = function () {
$scope.data = generatedata(12);
};
});

It contains the definitions of the grid data, source and data adapter. The function $scope.reloadGrid is invoked, through the directive ng-click, when the button is clicked. It changes the value of the $scope.data array. The change is handled in the ng-jqxgrid directive. Unlike Example #1, here it is defined in the local script:

// jqxGrid custom directive
demoApp.directive('ngJqxgrid', function ($interval, dateFilter) {
function link(scope, element, attrs) {
var isInitialized = false;
var propertyToScopeArray = propertyToScope(attrs.ngJqxgrid);
for (var p = 0; p < propertyToScopeArray.length; p++) {
var property = propertyToScopeArray[p].scopeVar;
eval("var " + property + " = scope." + property + ";");
};
var settings = eval("(" + attrs.ngJqxgrid + ")");
$(document).ready(function () {
$(element[0]).jqxGrid(settings);
isInitialized = true;
});
scope.$watch("data", function (newValue, oldValue) {
if (isInitialized == true) {
scope.source.localdata = newValue;
scope.dataAdapter = new $.jqx.dataAdapter(scope.source);
$(element[0]).jqxGrid({ source: scope.dataAdapter });
};
});
};
return {
link: link
};
});

For a better efficiency, the directive utilizes the function propertyToScope, also used by the aforementioned ng-jqxsettings directive from Example #1. It returns an array of key-value pairs from the settings string passed to ng-jqxgrid. The grid is then initialized with the settings. To this point, everything is similar to ng-jqxsettings. The different part is the scope.$watch handler. Here, the directive "watches" only for changes of scope.data, which happen by clicking the reload button. When data is changed, the grid source and data adapter are changed in turn and applied. This results in reloading the grid.

angularjs grid

Example #3: Initializing a Widget with a Directive for Each Property

Our third example presents a rather different approach from #1 and #2. Instead of having a single directive, containg the grid settings, here we have a separate directive (attribute) for each property we want to set. Additionally, we have a directive denoting the widget to initialize. The example, showing a jqxTabs instance, can be found here: jqwidgets-angularjs-directives.htm. This may sound like a lot of code, but, actually, we will define only the widget directive, in which we will use the values of the property directives. But, first, here is the View of our Angular page:

<body class='default' ng-controller="tabsController">
<div id="jqxTabs" ng-model="selectedTab" ng-jqxtabs ng-jqxtabs-width="550px" ng-jqxtabs-height="150px"
ng-jqxtabs-theme="shinyblack" ng-jqxtabs-position="bottom" ng-jqxtabs-selection-tracker="false">
<ul style='margin-left: 20px;'>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
<li>Tab 4</li>
<li>Tab 5</li>
</ul>
<div>
Content 1
</div>
<div>
Content 2
</div>
<div>
Content 3
</div>
<div>
Content 4
</div>
<div>
Content 5
</div>
</div>
<p>
Selected Tab: <strong>{{selectedTab}}</strong></p>
</body>

As you can see, each property directive (such as ng-jqxtabs-width) has the value we want to set to that particular property. The widget directive ng-jqxtabs has no value. Also note the ng-model directive, which is bound to the scope variable selectedTab. By default, different input types and other form controls can be bound to scope variables through ng-model. With our custom directive, we will also be able to extend ng-model's functionality so that it can be applied to a widget, too.

Here is the code of ng-jqxtabs:

demoApp.directive('ngJqxtabs', function ($interval, dateFilter) {
function link(scope, element, attrs) {
var tab = $("#" + attrs.id);
var theme = attrs.ngJqxtabsTheme;
var width = attrs.ngJqxtabsWidth;
var height = attrs.ngJqxtabsHeight;
var position = attrs.ngJqxtabsPosition;
var selectionTracker = attrs.ngJqxtabsSelectionTracker == "true" ? true : false;
$(document).ready(function () {
tab.jqxTabs({ theme: theme, width: width, height: height, position: position, selectionTracker: selectionTracker });
tab.on("selected", function (event) {
scope[attrs.ngModel] = event.args.item + 1;
scope.$apply();
});
});
};
return {
link: link
};
});

All we have to do is get the values of the property attributes and use those settings to initialize the tabs in $(document).ready(). Finally, in the selected event binding, we update the ng-model variable (selectedTab) according to the tab selection.

angularjs tabs

Additional Examples

Here are some more examples, featuring different controls from the jQWidgets library and following the approaches presented in Examles #1-3: