Archive for ‘jquery’

September 25, 2012

ASP.NET MVC with Ajax calls to controller actions

Recently I was trouble shooting an ASP.NET MVC project which relies on some Ajax calls to controller
actions. The project has a view in which there are a couple of drop down lists which trigger an
action result as soon as an item is selected in each list.

The view has a drop down list defined as follows:

@Html.DropDownList("searchTemplate",
new SelectList(Model.DefaultSearchTemplates, "SearchFilterTemplateId", "Name"), "Choose")

And a change event triggers the action result:

$("select#searchTemplate").change(function () {
    var selectedTemplate = $("select#searchTemplate").val();
    
    if (selectedTemplate) {
	$.ajax({
	    url: '@Url.Action("ApplySearchTemplate","Quote")',
	    data: { templateId: selectedTemplate }
	});
    }
});

The functionality of the Action isn’t important for this post, but it essentially saves a users
last search criteria so that the next time they log in, they will see the same results.

The problem with the application was that the selection of a search template only worked once. And
on further inspection, it turned out that the controller action was not being called at all after
the first time.

This was an interesting problem because the change event was certainly being called every time that
an item was selected in the list. My initial thought was that there must be some bug in ajax itself,
but it did seem fairly unlikely that such a bug would have gone un-noticed.

As expected, after digging through the ajax source, the problem became apparant; user error. Ajax
caches requests by default and even though these particular calls are not returning values nor
expecting results; the requests are still cached.

Since the requests were cached, each subsequent selection in the dropdown list resulted in ajax
“ignoring” the request since it “already knew the results”.

Once I understood what was happening, it was just a matter of disabling ajax caching for the view.
There are two ways to disable the cache; the first is to disable it individually for each request:

$("select#searchTemplate").change(function () {
    var selectedTemplate = $("select#searchTemplate").val();
    
    if (selectedTemplate) {
	$.ajax({
	    url: '@Url.Action("ApplySearchTemplate","Quote")',
	    data: { templateId: selectedTemplate },
	    cache: false
	});
    }
});

The second option is to globally disable ajax caching using ajaxSetup:

$.ajaxSetup({ cache: false });

Since this particular view has multiple similar calls to controller actions, setting it globally was
the solution that was chosen.

Advertisements
June 18, 2012

A simple drawing grid with HTML5 part 4

This week, we will complete the simple drawing grid by adding support for mobile browsers. Support for mobile browsers consists of enabling interaction with the canvas with touch events rather than the mouse events that are currently used.
The first step however is to add some detection for whether or not the client is using a mobile device. There really isn’t a standard way to accomplish this that I am aware of, but we can use a bit of javascript to do the job.

var isMobile = {
    Android: function() {
        return navigator.userAgent.match(/Android/i) ? true : false;
    },
    BlackBerry: function() {
        return navigator.userAgent.match(/BlackBerry/i) ? true : false;
    },
    iOS: function() {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i) ? true : false;
    },
    Windows: function() {
        return navigator.userAgent.match(/IEMobile/i) ? true : false;
    },
    Any: function() {
        return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
    }
};

This is not a comprehensive detection of all mobile devices, but should cover the most popular ones.

Once we determine if the client is using a mobile browser, we can choose wheter or not to bind mouse events or touch events. This is a simple matter of adding an if condition when the page has been loaded.

if (!isMobile.Any()) {
	alert('not mobile');
	$("#grid").mousedown(function(e) {
		mouseDown = true;
	}).bind('mouseup', function() {
		mouseDown = false;
	}).bind('mouseleave', function() {
		mouseDown = false;
	});
	
	$("#grid").mousemove(function(e) {
		if (mouseDown) {
			drawOnCanvas(e);
		}
	});
} else {
	grid_canvas.addEventListener('touchstart', function(e) {
		mouseDown = true;
	});
	
	grid_canvas.addEventListener('touchend', function(e) {
		mouseDown = false;
	});
	
	grid_canvas.addEventListener('touchmove', function(e) {
		e.preventDefault();
		if (mouseDown) {
			drawOnCanvas(e);
		}
	});
}

You should note that by default, some mobile browsers have the default behavior for ‘ touchmove’ already defined to move the entire browser window. We can disable this by adding the following to the ‘touchmove’ event that is bound to the canvas.

e.preventDefault();

Now that we have detection and handling for mobile browsers, the drawing grid is supported in both mobile and non-mobile web browsers. As an exercise, you may choose to increase the size of the canvas and controls to make it more visible for mobile devices with smaller screens.

The entire source for our completed drawing grid application can be seen by viewing the source of the demo.

June 14, 2012

A simple drawing grid with HTML5 part 3

This is a continuation of our simple drawing grid series. Last week, we ended up with a functional drawing grid with a few expected controls like setting pen size and pen color. While it was functional, it left a lot to be desired from a presentation standpoint. This week, we will work on giving our application a little bit of style with css.
First, we will want to arrange the HTML a bit to support our controls being rendered to the right of the grid rather than below. This could be done with css, but for the purpose of simplicity, we will do it with a good old fashioned table.

<table>
	<tr>
		<td rowspan="2">
			<div id="container">
				<canvas id="grid">
					<p>Your browser does not support HTML5 canvas</p>
				</canvas>
			</div>
			<script type="text/javascript" src="scripts/grid.js"></script>
		</td>
		<td>
			<!-- controls -->
		</td>
	</tr>
</table> 

Instead of using buttons, to improve the appearance, we will use images for some of the controls. For the pen size selection, I chose to use text. For this, you can simply open up your favorite image editor (I chose gimp), select your preferred font, type in your label and save the image. For the other controls, you can simply use google to find images available for free via the creative commons license.
Once you have selected the images, all that is required is to change the input type of the elements from button to image and add a source path. I’ve created an images folder for this purpose, but you can place them anywhere that you choose.

<div><input type="image" src="images/eraser.png" title="erase" onclick="setEraser()" ></div>

If you remember from last week, we were using the mousedown event on any button other than mouse button one to provide the eraser functionality. To make it more intuitive, a control has been added to provide that functionality instead.
The process of choosing colors by defining a button or image input per color as we did last week isn’t very scalable. To solve this problem, we will add a color selector to the the controls section. We’ll discuss the implementation a bit later, but will define the element while working with the html.

<input type="color" id="colorpicker" name="colorpicker" /> 

Now that we have all of the controls defined, the html table should look similar to the following.

<td rowspan="2">
	<div id="container">
		<canvas id="grid">
			<p>Your browser does not support HTML5 canvas</p>
		</canvas>
	</div>
	<script type="text/javascript" src="scripts/grid.js"></script>
</td>
<td>
	<div><input type="image" src="images/smallPen.png" title="small pen" onclick="setPenSize(2)" ></div>
	<div><input type="image" src="images/mediumPen.png" title="medium pen" onclick="setPenSize(4)" ></div>
	<div><input type="image" src="images/largePen.png" title="large pen" onclick="setPenSize(8)" ></div>
	<div><input type="image" src="images/eraser.png" title="erase" onclick="setEraser()" ></div>
	<div><input type="image" src="images/trash.png" title="clear" onclick="initCanvas()" ></div>
	<input type="color" id="colorpicker" name="colorpicker" />
</td> 

Last week, our canvas used a blue background color so that it was clearly visible on the page. To improve the appearance, we will use a white background and use css to define a border. To do this, create a folder called css and an empty text file within it named grid.css. Then simply use the following to create a nice border around the canvas

#container {
	width: 400px; margin: 0px 12px 24px 12px;		
	box-shadow: 0px 5px 25px #343434;
	-moz-box-shadow: 0px 5px 25px #343434;
	-webkit-box-shadow: 0px 5px 25px #343434;
	-webkit-border-radius: 10px;
	-moz-border-radius: 10px; 
	border-radius: 10px;
}

Next, add a reference to the css script in the head section of the page.

<link rel="stylesheet" href="css/grid.css" type="text/css">

The last step is to implement the color picker functionality. Rather than re-inventing this functionality, we will use the fantastic color picker called Spectrum from Brian Gridstead. After evaluating several freely available color picker controls, Spectrum proved to be far and away the easiest to implement, most compatible with all of the major browsers, and feature complete.
The first step in implementing Spectrum is to download spectrum.js and spectrum.css. I’ve placed them in the scripts folder and the css folder respectively. Once the files are in place, add them to the page.

<script src="scripts/spectrum.js" type="text/javascript"></script>
<link rel='stylesheet' href='css/spectrum.css' type="text/css">	

And finally, bind the extension to the “colorpicker” element that we defined earlier while creating the controls section. To do this, simply add the following to grid.js .ready function.

$("#colorpicker").spectrum({
	color: "#0000ff",
	change: function(color) {
		penColor = color.toHexString();
	},
	show: function(color) {
		erase = false;
	}
});

As you can see, the change event of the color picker sets our pen color and the show event disables the erase functionality. Be sure to spend some time reviewing the excellent documentation provided on the Spectrum website to learn more about all of the options available with the extension.
For now, we have completed our face lift for the drawing application. The full source code is available via the demo.

May 29, 2012

A simple drawing grid with HTML 5

This week, we will build a simple drawing grid using HTML5 and javascript. In the coming weeks we will transform this simple example into a fully functional drawing application. For this demo, the entire browser window will be a canvas and resizing or reloading the window will clear it.
The first step is to define our canvas and add some css that will set the size of the canvas to the size of the browser window.

<!DOCTYPE HTML>
<html>
	<style type="text/css">
	html, body { width: 100%; height: 100%; margin: 0px; }
	</style>
	<body>
		<canvas id="grid">
			<p>Your browser does not support HTML5 canvas</p>
		</canvas>
	</body>
</html>

This is all that is required to create a canvas that consumes the entire browser window. The next step is to add javascript to the page so we can have an interactive drawing surface. We will be using jquery and loading a script named grid.js; which will perform the canvas updates.

<!DOCTYPE HTML>
<html>
	<head>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
	</head>
	<style type="text/css">
	html, body { width: 100%; height: 100%; margin: 0px;  }
	</style>
	<body>
		<canvas id="grid">
			<p>Your browser does not support HTML5 canvas</p>
		</canvas>
		<script type="text/javascript" src="scripts/grid.js"></script>
	</body>
</html>

The next step is to create a folder named “scripts” and within it create a text file called grid.js using your favorite editor. The grid.js script will be responsible for all of the canvas updating. For starters, we will need to define the callbacks for window events in order to initialize the canvas data. “window.onresize” will be used so that the canvas can be cleared when the window is resized.

window.onload = window.onresize = function() {
	var grid_canvas = document.getElementById("grid");
	var ctx = grid_canvas.getContext("2d");
	var viewWidth = window.innerWidth;
	var viewHeight = window.innerHeight;
	var backgroundColor = "rgb(0,0,255)";

	grid_canvas.style.position = "fixed";
	grid_canvas.setAttribute("width", viewWidth);
	grid_canvas.setAttribute("height", viewHeight);
	grid_canvas.style.top = 0;
	grid_canvas.style.left = 0;

	ctx.clearRect(0,0,viewWidth,viewHeight);
	ctx.fillStyle = backgroundColor;
	ctx.fillRect(0,0,viewWidth,viewHeight);	
}

At this point, the canvas is created, but isn’t very interesting. To draw on the canvas, we will rely on mouse events. In this demo, holding down the left mouse button and dragging will fill in cells of the grid that the mouse is hovering over. Holding any other mouse button down and dragging will be used for an “erase” function. It’s not actually erasing, but coloring the cells in with the defined background color. The following should be added to the window event callback defined above.

var xOffset = $("#grid").offset().left;
var yOffset = $("#grid").offset().top;
var mouseDown = false;
var mouseButton = 0;
var penColor = "rgb(255,255,255)";
var penWeight = 5;

$("#grid").mousedown(function(e) {
	mouseDown = true;
	mouseButton = e.which;
}).bind('mouseup', function() {
	mouseDown = false;
});

$("#grid").mousemove(function(e) {
	if (mouseDown) {
		var x = Math.floor((e.pageX-xOffset) / penWeight);
		var y = Math.floor((e.pageY-yOffset) / penWeight);

		ctx.fillStyle = mouseButton == 1 ? penColor : backgroundColor;
		ctx.fillRect(x*penWeight, y*penWeight, penWeight, penWeight);
	}
});

Loading the page now should result in a functional drawing application. Changing the “penWeight” variable will alter the cell sizes on the hidden grid. As a small optimization, you may consider storing the last cell that was drawn to and skipping a redraw (included in the source example) if the user is still moving with the cell. At smaller cell sizes, this becomes less important.

The demo for the grid can be found here.