You are browsing the archive for Dojo.

Dojo >= 1.7 Kickstart

November 3, 2013 in Dojo

This presentation targets the people who wants to learn the basics of Dojo (>= 1.7).

TypeError: invalid ‘in’ operand obj

September 16, 2013 in Ajax, Dojo, Web 2.0

A weird JavaScript error that you may face when using Dojo Ajax for retrieving some JSON data from the server to render on the browser. This error happens because Dojo is unable to process the resulted JSON data which is the case when handleAs attribute is set to “text”.

In order to fix this error, set handleAs attribute to “json” instead of “text” as shown below in the example:

xhr.get({
	url: '/SomeServlet',
	handleAs: "json",
	timeout: 3000,
	load: function(data) { /* Success Handling */ },
	error: function(err) { /* Failure Handling */ }
});

This is my today’s two cent advice.

Dojo Ajaxified multi-file upload (including IE7)

June 25, 2013 in Dojo, JavaScript, Web 2.0

One of the most common requirements of business applications is to develop Ajaxified multi-file upload. Thanks to Dojo 1.6 (and later), you can achieve this requirement, however, there are some tricks that you need to do in order to have this feature developed across all the browsers including IE. Let’s start to see the code.

The following listing shows the HTML code part of the file uploader.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>File Upload POC</title>
	
	<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js">
	</script>
</head>
<body>
	<script type="text/javascript" src="${pageContext.request.contextPath}/js/fileUploader.js"></script>
	<div id="container">
		<form method="post" id="myForm" enctype="multipart/form-data">
		    <fieldset>
		        <legend>Form Files Test</legend>
		        
		        <input id="clearBtn" type="button" value="Clear"></input><br/><br/>
				<div id="uploader"></div><br/>
				<div id="uploaderStatus"></div>
      					
		        <input id="uploadBtn" type="button" value="Submit"></input>
		    </fieldset>
		</form>
	</div>
</body>
</html>

As shown the file upload HTML, the form’s enctype needs to be set to “multipart/form-data”. The form contains “uploader” div that represents the Ajaxified file upload component and “uploaderStatus” div that represents the status panel of the selected files and finally it contains the upload button that will be used for uploading.

Now, Let’s look at the JavaScript file (fileUploader.js) which uses the dojos/form/Uploader module.

require([
        "dojo/parser", "dojox/form/Uploader", "dojo/dom", "dojo/on", "dojo/has", 
        "dojox/form/uploader/FileList", "dojox/form/uploader/plugins/IFrame", 
        "dojo/domReady!"], 
function(parser, Uploader, dom, on, has) {    
    parser.parse(document.getElementById("container"));
    
    var uploaderDIV = document.getElementById("uploader");
    
    var up = new dojox.form.Uploader({
        label: 'Select files',
        style : 'background-color: #ddddff; border: solid 1px;', //Externalize ...
        multiple: true,
        url: "/multifile-ajax-poc/UploaderServlet"
    }).placeAt(uploaderDIV);
    
    on (dom.byId("uploadBtn"), "click", function(evt) {
    	
    	//You can put some validations here ...
        up.submit();
    });  

    on (dom.byId("clearBtn"), "click", function(evt) {
        dom.byId("uploaderStatus").innerHTML = "";
        up.reset();
    });
    
    dojo.connect(up, "onComplete", function(dataArray) {
        var i = 0;
        
        dom.byId("uploaderStatus").innerHTML = "";
        
        if (!dataArray.error) {
            for (i = 0; i < dataArray.length; ++i) {
            	dom.byId("uploaderStatus").innerHTML += "File ID is: " + dataArray[i].id + " is uploaded" + "<br/>";
            }
        } else {
        	dom.byId("uploaderStatus").innerHTML = "Unable to upload the file(s)";
        }
    });    
    
    dojo.connect(up, "onChange", function(evt) {
        var i = 0;
        var content = "";
        var dataArray = up.getFileList();
        
        for (i = 0; i < dataArray.length; ++i) {
            content += dataArray[i].name + "<br/>";
        }
        
        dom.byId("uploaderStatus").innerHTML = content;
    });    
    
    up.startup();     
}
);

As shown in the code, the Dojo Ajaxified file uploader is created using dojox.form.Uploader constructor with specifying the main important attributes:
1. label.
2. multiple attribute is set to true in order to support multi-file uploading.
3. url attribute is set to the Java Servlet which handles the file uploading.

In order to get the uploader working fine, you need to call startup() method of the component.

There are important methods that are needed to be considered:
1. submit() method which allows submitting the selected files to the Java Servlet specified in the url attribute.
2. reset() method which is used to clear the selected files. Note that one of the limitations of this component is that it does not allow removing the selected files individually so the only option you have is ti clear all the files and select them again.
3. getFileList() method which is used to get the list of selected files that you can display for example in the “on change” event of the component as shown by the example.

One important thing to note is dojox/form/uploader/plugins/IFrame plugin which is essential for non-HTML5 browser in order to perform Ajaxified file uploads. Using this plugin will allow file uploading to be done through iframe for poor browsers like IE7.

Finally, let’s come to the most important trick of Ajaxified file uploading which is the server side part. The following code listing shows UploaderServlet Java Servlet.

package servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploaderServlet")
public class UploaderServlet extends HttpServlet {
	private static final long serialVersionUID = 1809724554045451657L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		DiskFileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		String result = "";
		
		try {
			List<FileItem> items = upload.parseRequest(request);
			Iterator<FileItem> iter = items.iterator();
			int index = 0;

			result += "[";
			
			while (iter.hasNext()) {
			    FileItem item = iter.next();

			    if (!item.isFormField()) {
				    if (index != 0) {
				    	result += ", ";
				    }
				    
			        String feedback = processUploadedFile(item);
			        
			        //Handle IE7 ugly uploading bug
			        if (feedback == null) {
			        	continue;
			        } else {
			        	result += feedback;
			        }
			        
				    ++index;
				    
				    System.out.println(index);
			    }
			}
			
			result += "]";
			
			System.out.println(result);
			
		} catch (FileUploadException e) {
			result = "{'error':'" + e.getLocalizedMessage() + "'}";
			e.printStackTrace();
		}
		
		respondToClient(request, response, result);
	}

	private String processUploadedFile(FileItem item) {
		byte[] data = item.get();
	    String fileName = item.getName();
	    String contentType = item.getContentType();
	    
	    // Handle IE7 file uploading ugly bug ...
	    if (fileName.equals("")) {
	    	return null; //ignore
	    }
	    
		System.out.println(fileName + ", " + contentType + ", " + data.length);	    
	    
	    return "{'fileName':'" + fileName + "', " + 
	    		"'contentType':'" + contentType + "', " + 
	    		"'size':" + data.length + ", " + 
	    		"'id':" + (System.currentTimeMillis() + new Random().nextInt(100)) + "}";
	}
	
	private void respondToClient(HttpServletRequest request, HttpServletResponse response, String result) throws IOException {
        response.setContentType("text/html");
		PrintWriter writer = response.getWriter();		
		String browser = request.getHeader("User-Agent");
		
		if (browser.indexOf("MSIE 9") &gt; 0 || browser.indexOf("MSIE 8") &gt; 0 || browser.indexOf("MSIE 7") &gt; 0 ) {
			
			// For IE 9, 8, and 7 browser, render JSON object inside text area ...
			//String sampleOutput = "<textarea>[{'success':'true', 'id':'123456789'}]</textarea>";
			writer.write("<textarea>" + result + "</textarea>");
		} else {
			
			//For non-IE browsers, render normal JSON objects.
			//String sampleOutput = "[{\"success\":\"true\", \"id\":\"123456789\"}]";
			writer.write(result.replace("'", "\""));
		}		

		writer.flush();		
	}
}

Our UploaderServlet main purpose is to receive the files and if it succeeds to process the files, then it returns a simple array with the basic information of the files that are successfully uploaded.

The most important thing to notice here is for IE7 and IE8 and IE9 (NOT IE 10), a special handling is needed to be taken into consideration in the Servlet response:
1. The response data must be wrapped into textarea field.
2. Do not use double quotes (“) inside the returned JSON array, instead use single quote (‘).

In other modern browsers (which support HTML5 file uploading), you do not need to wrap the content in textarea as it does not use iframe for uploading, and you can freely use the double quotes (“) in the JSON array.

There is an ugly bug that you need to take care about in IE 7 and 8 and also 9 with the Dojo file uploader component, this bug is about having a redundant file with non-name that is always sent in the request of the multiple-file Ajax uploading. In order to handle this bug, just ignore the file which does not have a name (Ugly problem and ugly fix :-)).

This is all about my experience in this component, I wish that it can be helpful to you.

Download the code sample

Resolving the virtual keyboard popup issue in Android 2.x browsers

April 12, 2013 in Dojo, JavaScript, mobile

One of the alarming issues of the Android 2.x browsers is that virtual keyboard sometimes becomes open even if the cursor is not focused on an editable field.
This symptom can be extended to a degree that the virtual keyboard can be still open in views other than the one that initially shows the keyboard.

After spending hours in trying to figure out a solution to this problem, I tried the following solutions and none of them worked with me:
1. Disabling the input elements in the form temperaroly.
2. Setting the input elements in the form to read-only temporarily.
3. Blurring the current focused editable field does not also work.

The only way that works with me is to send an enter key to one of the text fields in the form (or to any hidden field) after 1000 milliseconds before performing the transition to any other views, this can be done in Dojo mobile as follows:

window.setTimeout(function() {

    registry.byId("[your dojo/mobile/textbox id]")._onInput({ charOrCode: keys.ENTER });

}, 1000);

If you have a better solution for this issue, let me know.

Resetting dojox.mobile.ScrollableView to top

April 2, 2013 in Dojo, JavaScript, mobile, Web 2.0

ScrollableView is a container widget which represents an entire mobile device screen, and has a touch scrolling capability. Sometimes, you may need to reset ScrollableView to the top or to any position in the mobile screen.
In order to achieve this requirement, you need to use its scrollTo() API, for example, scrolling to top can be done by setting x and y to zero as follows:

require(["dojox/mobile/parser", "dijit/registry", ..., "dojo/domReady!"], function(parser, registry) {
	//...
	var view = registry.byId("someViewID");
	view.scrollTo({x:0 ,y: 0});
	//...
});

Following this approach, you can scroll ScrollableView to any position you like by specifying a suitable x and y values.

Getting Yahoo! Weather from JavaScript ONLY

March 18, 2013 in Dojo, JavaScript

Getting weather information in mashup applications is a very common task. As you may know, Yahoo! thankfully provided a REST API that can be used in order to get weather information using location’s zip code.
In order to invoke the Yahoo! weather REST API from JavaScript client, you can either create an XHR request that talks with a local server proxy which deals with the Yahoo! weather REST API (in order to avoid the security exception of cross-domain Ajax requests on the JavaScript client) or to use JSONP which is supported by the Yahoo! Query Language. In this post, the second approach will be illustrated because it does not require any server side work.

Dojo has a "dojo/request/script" module that can handle interactions with JSONP endpoints as follows:

require(["dojo/request/script"], function(script){
  script.get(URL, {
    jsonp: "callback",
    preventCache: true,
    timeout: 3000
  }).then(function(response) {
    // Do something with the response data
  }, function(error) {
    // Handle the error
  });
});

script object has a get API that has two parameters:
1. JSONP Service URL.
2. JSON object with optional parameters. Optional parameters can be:
a. jsonp: The name of the JSONP query parameter.
b. preventCache for cache prevention.
c. timeout: The number of milliseconds to wait for the response. If this time passes the request is canceled and the promise rejected.

For the complete list of optional parameters, check Dojo documentation here.

This is all about what you need to know about this API. Now, let’s see how to retrieve the weather information of an example place (New Era – Michigan) which has the following zip code 49446.

var errorMessage = "Unable to get weather information.";

var zipcode = "49446"; //New Era - Michigan

var query = "q=" + escape(
		  "select item from weather.forecast where location") + 
		  "=\"" + zipcode + "\"";

var endPointURL = "http://query.yahooapis.com/v1/public/yql?" + query  + 
		  "&format=json";


script.get(endPointURL, {
	jsonp: "callback",
	preventCache: true,
	timeout: 3000
}).then(function(response) {
	try {
		document.getElementById("someDivID").innerHTML = response.query.results.channel.item.description;
	} catch (exception) {
		alert(errorMessage);
	}
}, function(error) {
	alert(errorMessage + " Make sure that your device is connected to the internet.");
	console.log(error); 
});

endPointURL includes the Yahoo! REST service URL with the needed parameters and the request timeout parameter is set to 3 seconds.

Once the JSON response is retrieved successfully from the server, it will look like the following:

dojo.io.script.jsonp_dojoIoScript2._jsonpCallback({"query":{"count":1,"created":"2013-03-18T02:34:06Z","lang":"en-US","results":{"channel":{"item":{"title":"Conditions for New Era, MI at 9:33 pm EDT","lat":"43.56","long":"-86.35","link":"http://us.rd.yahoo.com/dailynews/rss/weather/New_Era__MI/*http://weather.yahoo.com/forecast/USMI0597_f.html","pubDate":"Sun, 17 Mar 2013 9:33 pm EDT","condition":{"code":"33","date":"Sun, 17 Mar 2013 9:33 pm EDT","temp":"24","text":"Fair"},"description":"\n<img src=\"http://l.yimg.com/a/i/us/we/52/33.gif\"/><br />\n<b>Current Conditions:</b><br />\nFair, 24 F<BR />\n<BR /><b>Forecast:</b><BR />\nSun - Mostly Clear. High: 29 Low: 22<br />\nMon - PM Snow. High: 37 Low: 23<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/New_Era__MI/*http://weather.yahoo.com/forecast/USMI0597_f.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>\n","forecast":[{"code":"33","date":"17 Mar 2013","day":"Sun","high":"29","low":"22","text":"Mostly Clear"},{"code":"16","date":"18 Mar 2013","day":"Mon","high":"37","low":"23","text":"PM Snow"}],"guid":{"isPermaLink":"false","content":"USMI0597_2013_03_18_7_00_EDT"}}}}}});

This is why using the response.query.results.channel.item.description long expression will get the required weather information from the returned service response.

Changing the moveTo attribute in “dojox/mobile/Heading” dynamically

January 29, 2013 in Dojo, HTML5, JavaScript, mobile, Web 2.0

One of the known issues of the Dojo mobile is that when the Dojo mobile heading dojox.mobile.Heading has a moveTo attribute, the value of the moveTo attribute cannot be changed once the widget is created. This means that the moveTo attribute will cause the back button to be always going to the initial view id that is binded to the attribute.

In order to resolve this issue (to support, for example, dynamic back navigation), you can destroy the widgets inside the dojox.mobile.Heading and then re-create the widgets again. Let’s see how to do this. The following code snippet shows the mobile heading element in the HTML file:

<div>
    <h1 data-dojo-type="dojox/mobile/Heading" data-dojo-attach-point="dynamicHeading">
    </h1>
    <!-- Other elements -->
</div>			

And in the "onBeforeTransitionIn" of the view, you can call the buildHeadingDynamic("someView") function which destroys the descendants of the heading and then re-create it again with the new moveTo value.

buildHeadingDynamic : function(backView) {
  var dynamicHeading = this.dynamicHeading;
  dynamicHeading.destroyDescendants();

  var heading = new dojox.mobile.Heading ({label:"Heading", fixed:"top", back:"Back", moveTo:"#" + backView});
  dynamicHeading.addChild(heading);		
}

Parsing ATOM/RSS feeds from JavaScript code using Dojo

August 22, 2012 in Dojo, JavaScript

One of the most common requirements is to parse RSS and ATOM feeds from the JavaScript code. In this post, I show you how to parse both RSS and ATOM feeds from your JavaScript code using Dojo.

Parsing ATOM feed

The next code listing is an example which loads and parses the ATOM feed from the "atom.xml" file that contains an ATOM XML.

<script type="text/javascript">
dojo.addOnLoad(function() {
	  var resultPanelID = "resultPanel";
	  
	  // Parse ATOM feed
	  dojo.xhrGet({
	     url: "atom.xml",
	     preventCache: true,
	     handleAs: "xml",
 	     load: function(xmlDoc, ioArgs){		 
	         var i = 0;
	         var output = "";
	      
	         var node = xmlDoc.getElementsByTagName("feed").item(0);
	     
	         if (node == null) {
		         console.debug("ATOM XML format is corrupted ...");		    	     
	    	     return;
	         }
	     
	         var entriesLength = node.getElementsByTagName("entry").length;
	     
			 for (i = 0; i < entriesLength; ++i) {
				var entry = node.getElementsByTagName('entry').item(i);
			 	
				var title = entry.getElementsByTagName('title').item(0).firstChild.data;
			 	var published = entry.getElementsByTagName('published').item(0).firstChild.data;			
			 	var summary = entry.getElementsByTagName('summary').item(0).firstChild.data;
			 	var link = entry.getElementsByTagName('link').item(0).getAttribute("href");
			 	
				output += '<hr><p><a target="_blank" href="' + link +'">' + title + '</a><br/>' + 
					      '<span class="smaller">' + published + '</span><br/>' + 
					      summary +
					      '</p>';
			 }
			 
		     document.getElementById(resultPanelID).innerHTML = output; 	    	
         },
         error: function(error, ioArgs){		         
	         dojo.byId(resultPanelID).innerHTML = "Error loading atom feed";
	         console.debug("failed xhrGet for ATOM URL: ", error, ioArgs);	         
    	 }
	});
});	
</script>

<div id="resultPanel"></div>

The code listing gets all the <entry> elements in the ATOM feed then gets some of the child elements of the <entry> element which are the <title>, <published>, <summary>, and <link> elements and then displays their values in the "resultPanel" DIV element.

Parsing RSS feed

The next code listing is an example which loads and parses the RSS feed from the "rss.xml" file that contains an RSS XML.

<script type="text/javascript">
dojo.addOnLoad(function() {
	  var resultPanelID = "resultPanel";
	  
	  // Parse Rss Feed
	  dojo.xhrGet({
	     url: "rss.xml",
	     preventCache: true,
	     handleAs: "xml",
	     load: function(xmlDoc, ioArgs){		 
	         var i = 0;
	         var output = "";
	      
	         var node = xmlDoc.getElementsByTagName("channel").item(0);
	     
	         if (node == null) {
		         console.debug("RSS XML format is corrupted ...");	  		    	     
	    	     return;
	         }
	     
	         var entriesLength = node.getElementsByTagName("item").length;
			 
			 for (i = 0; i < entriesLength; ++i) {
				var entry = node.getElementsByTagName('item').item(i);
			 	
				var title = entry.getElementsByTagName('title').item(0).firstChild.data;
			 	var published = entry.getElementsByTagName('pubDate').item(0).firstChild.data;			
			 	var description = entry.getElementsByTagName('description').item(0).firstChild.data;
			 	var link = entry.getElementsByTagName('link').item(0).firstChild.data;
			 	
				output += '<hr><p><a target="_blank" href="' + link +'">' + title + '</a><br/>' + 
					      '<span class="smaller">' + published + '</span><br/>' + 
					      description +
					      '</p>';
			 }
			 	
		     document.getElementById(resultPanelID).innerHTML = output; 	    	
       },
       error: function(error, ioArgs){	         
	         dojo.byId(resultPanelID).innerHTML = "Error loading Rss feed";
	         console.debug("failed xhrGet for Rss URL: ", error, ioArgs);	  	         
  	   }	  
	});
});	
</script>

<div id="resultPanel"></div>

The code listing gets all the <item> elements in the RSS feed then gets some of the child elements of the <item> element which are the <title>, <pubDate>, <description>, and <link> elements and then displays their values in the "resultPanel" DIV element.

Could not convert JavaScript argument arg 0 [nsIDOMHTMLDivElement.appendChild]

August 1, 2012 in Dojo, JavaScript, Web 2.0

You may face this error when you add a Dojo component to a normal DOM element using appendChild as follows:

<div id="container"></div>

<script type="text/javascript">
	dojo.addOnLoad(function(){
		var dojoInputField = new dijit.form.TextBox({id: "txtName"});
		document.getElementById("container").appendChild(dojoInputField);
	});
</script>

This error appears because you try to add the Dojo element itself not the DOM node that it wraps. In order to fix this issue, you can do this by adding the component DOM node as follows:

<div id="container"></div>

<script type="text/javascript">
	dojo.addOnLoad(function(){
		var dojoInputField = new dijit.form.TextBox({id: "txtName"});
		document.getElementById("container").appendChild(dojoInputField.domNode);
	});
</script>	

Changing the default value of Dojo DateTextBox

July 1, 2012 in Dojo, JavaScript

In some business use cases, you may need to change the default value of the Dojo DateTextBox. For example, if you have a date of birth field and you wish to make the default date a date in the past in order to minimize the user navigation in the Dojo DateTextBox component.

In order to change the default value of Dojo DateTextBox, you need to change the value attribute of the dijit.form.DateTextBox as shown below.

...
<label for="sampleDate">Select the Date: </label>
<input id="sampleDate" dojoType="dijit.form.DateTextBox"></input>

...
<script type="text/javascript">
	dojo.addOnLoad(function (){
		dijit.byId('sampleDate').attr("value", new Date(1970, 1, 30)); 
	});
</script>
Skip to toolbar