Current Articles | RSS Feed
Dojo is a powerful user interface toolkit that lets developers build interactive web applications. In a previous article, I explained the steps you can take to create a custom Dojo component. While that article covered how to create the component structure, template, class, style, and how to test the component, the example I created was pretty simple. In this article, I will show how to create a more meaningful component that interacts with third-party APIs.
As you may remember from the previous article, the component template defines the HTML representation of the component, while the component class represents the component data and behavior. The elements of the component template can be referenced and used in the component class using the Dojo attach points.
Let's create a location viewer component that calls the Google Maps APIs to display the location of any valid address in the world on a map, and also states the latitude and longitude of the address. The component lets us configure the map zoom value, width, and height, and allows users to refresh a map by changing the address using the provided API.
We can declare the component within a page's HTML with the following syntax:
<div dojoType="custom.locationViewerWidget" id="viewer1" address="Cairo, Egypt" zoom="5" showLabel="true" containerWidth="500px" containerHeight="500px"/>
This declaration means that the component should display the location of the passed address ("Cairo, Egypt") on the map. The map has a zoom value equal to "5". The map width and height should be 500 pixels. Setting "showLabel" to true asks the LocationViewer component to show the address and its corresponding latitude and longitude above the map.
"5"
Let's start creating the LocationViewer component by creating the component template. The LocationViewer template has two main HTML elements under the root div element. The span element displays the address information and the div element displays the address location on the map.
div
<div> <span dojoAttachPoint="labelNode" class="locationWidgetLabel"></span> <div dojoAttachPoint="mapNode" style="width:${containerWidth}; height:${containerHeight}"> </div></div>
"labelNode" is used for referencing the span element, and "mapNode" is used for referencing the div element. The width and the height of the second div element are determined by the containerWidth and the containerHeight attributes, which as we'll see in a moment are defined in the widget class. The span element can be customized by modifying the "locationWidgetLabel" CSS class.
"labelNode"
"mapNode"
containerWidth
containerHeight
"locationWidgetLabel"
As I explained in the previous article, the dijit._Widget class is the base class for all of the dijit widgets, while the dijit._Templated class is responsible for reading HTML templates and creating the widget's DOM tree. The locationViewer widget class inherits from both the dijit._Widget and the dijit._Templated classes using the dojo.declare API. The next code listing shows the complete code of the widget class:
dijit._Widget
dijit._Templated
locationViewer
dojo.declare
dojo.provide("custom.locationViewerWidget");dojo.require("dijit._Templated");dojo.require("dijit._Widget");dojo.require("dijit.Tooltip");dojo.declare("custom.locationViewerWidget", [ dijit._Widget, dijit._Templated ],{ templatePath: dojo.moduleUrl("custom","templates/locationViewerWidget.html"), address: "", showLabel: false, zoom: 6, containerWidth: "300px", containerHeight: "300px", constructor: function() { }, postCreate: function() { var address = this.address; this.refreshMap(address); }, refreshMap: function(address) { if (address == "") { return; } var geocoder = new google.maps.Geocoder(); var localZoom = this.zoom; var localShowLabel = this.showLabel; var localLabelNode = this.labelNode; var mapOptions = { zoom: localZoom, mapTypeId: google.maps.MapTypeId.HYBRID }; var map = new google.maps.Map(this.mapNode, mapOptions); geocoder.geocode({'address': address}, function (results, status) { if (status == google.maps.GeocoderStatus.OK) { map.setCenter(results[0].geometry.location); var infoWindow = new google.maps.InfoWindow({ content: address }); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); infoWindow.open(map, marker); if (localShowLabel) { localLabelNode.innerHTML = "\"" + address + "\" has the following coordinates: " + results[0].geometry.location; } } else { alert("Error: " + status); } }); }});
As you can see, the component attributes are defined near the top in the dojo.declare statement. The postCreate method calls refreshMap, the main method provided by the locationViewerWidget class, passing the address attribute value to it as a parameter. The refreshMap API uses the Google Maps v3 APIs for rendering the map and for resolving the address to get the latitude and longitude corresponding to the address.
postCreate
refreshMap
locationViewerWidget
The google.maps.Map object creates a map. Its constructor takes two parameters: the DOM object (div) to generate the map content on, and the map options object. The DOM object is referenced by the mapNode Dojo attach point, while the map options object is populated with the map zoom value and the map type.
google.maps.Map
mapNode
The google.maps.Geocoder object is responsible for resolving the address by using the geocode API. This API takes two parameters: a JavaScript object that contains the address attribute, and a delegate function that is called once the geocoding operation is completed. The delegate function itself has two parameters that are filled when the operation is completed: the results object, which contains the latitude and the longitude of the location, and the status object, which represents the status code of the operation.
google.maps.Geocoder
geocode
address
results
status
The google.maps.Marker object creates a marker on the map. Its constructor takes two parameters: the map object and the position of the marker (latitude and longitude).
google.maps.Marker
The google.maps.InfoWindow object creates the Information window. Its constructor takes an object that contains the content attribute to be displayed. The information window is displayed when the open API is called.
google.maps.InfoWindow
content
open
Finally, if the showLabel attribute is set to true, the labelNode attach point HTML content is updated with the location and its information (latitude and longitude).
showLabel
labelNode
The widget style, defined in the custom.css file, applies to the labelNode span element. In our example it is a simple style class:
.locationWidgetLabel { font: bold 17px serif;}
To test the widget, you must register the locationViewerWidget module path in the HTML page using the dojo.registerModulePath statement, load the locationViewerWidget module using the dojo.require("custom.locationViewerWidget") directive, and include the custom.css style and the Google Maps API library script, as shown in the listing below:
dojo.registerModulePath
dojo.require("custom.locationViewerWidget")
<script src="dojo/dojo/dojo.js" djConfig="parseOnLoad: true"> </script> <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script> <link href="dojo/custom/css/custom.css" rel="stylesheet" type="text/css"/><script type="text/javascript"> dojo.registerModulePath("locationViewerWidget", "custom/locationViewerWidget"); dojo.require("dojo.parser"); dojo.require("dijit.form.Button"); dojo.require("custom.locationViewerWidget"); </script>
Continuing with the HTML code for our example page, we create the LocationViewer widget via a div element whose Dojo type is custom.locationViewerWidget, with initial values for the address and the zoom attributes:
custom.locationViewerWidget
Because the LocationViewer widget is a valid dijit widget, it can be retrieved at execution time using the dijit.byId function. The code listing below shows how to call the refreshMap API after retrieving its instance using the dijit.byId:
dijit.byId
<script type="text/javascript"> function changeAddress() { var viewer1 = dijit.byId("viewer1"); viewer1.refreshMap(document.getElementById("addressInput").value); }</script>...<label for="addressInput">Enter the address: </label><input id="addressInput" type="input"/><button id="changeLocationButton" dojoType="dijit.form.Button" onclick="javascript:changeAddress();"> Change location</button>
When a user clicks the changeLocationButton button, the browser executes the changeAddress function, which itself calls the refreshMap API of the widget, passing the user's entered address to it.
changeLocationButton
changeAddress
The listing below shows the complete testing code of the LocationViewer widget:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html> <head> <script src="dojo/dojo/dojo.js" djConfig="parseOnLoad: true"> </script> <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"> </script> <link href="dojo/custom/css/custom.css" rel="stylesheet" type="text/css"/> <script type="text/javascript"> dojo.registerModulePath("locationViewerWidget","custom/locationViewerWidget"); dojo.require("dojo.parser"); dojo.require("dijit.form.Button"); dojo.require("custom.locationViewerWidget"); </script> <link rel="stylesheet" type="text/css" href="dojo/dijit/themes/tundra/tundra.css"/> <script type="text/javascript"> function changeAddress() { var viewer1 = dijit.byId("viewer1"); viewer1.refreshMap(document.getElementById("addressInput").value); } </script> </head> <body class="tundra"> <label for="addressInput">Enter the address: </label> <input id="addressInput" type="input"/> <button dojoType="dijit.form.Button" onclick="javascript:changeAddress();"> Change location </button> <br/> <div dojoType="custom.locationViewerWidget" id="viewer1" address="Cairo, Egypt" zoom="5" showLabel="true" containerWidth="500px" containerHeight="500px"/> </body></html>
When you display the testing HTML page, you should see a page that looks like this:
For reference, you can download the complete component source.
I hope this article illustrates how custom Dojo components can not only help speed up development of rich Web 2.0 applications, but also help you maintain the applications, as Dojo centralizes the related presentation and logic code in one place (the component).
Allowed tags: <a> link, <b> bold, <i> italics