I was recently asked to develop a PIN Pad for an ASP.NET MVC3 mobile web application for users to use to log into the application. I had to use the PIN Pad in three locations in the application: 1) register the PIN for a new user, 2) confirming the PIN registration, and 3) logging in with the PIN for a registered user. Therefore, I decided to create an editor template for the PIN Pad so that I could easily use it in all three places. Three other goals I wished to accomplish with the editor template were to 1) allow for multiple PIN Pads to be added to a single page, 2) allow the PIN length to be variable without rewriting the control, and 3) allow developer that uses the PIN Pad in their web application to specify what occurs when the last digit is entered into the PIN Pad.
I started by creating a class to represent a PIN Pad object.
public class PinPad
{
public int PinLength { get; set; }
public string Pin { get; set; }
public string Prefix { get; set; }
public PinPad() { }
public PinPad(string prefix, int lengthOfPin)
{
this.Pin = string.Empty;
this.PinLength = lengthOfPin;
this.Prefix = prefix;
}
}
- PinLength –> The length of the PIN
- Pin –> Contains the user entered PIN
- Prefix –> contains a prefix used for the IDs and functions of the PIN Pad so that multiple PIN Pads can be added to a page
Then I created a PinPad.cshtml file in the Views->Shared->EditorTemplates folder
@model PinPadExample.Models.PinPad
@Html.HiddenFor(model => model.Prefix)
@Html.HiddenFor(model => model.Pin, new { @id = Model.Prefix + "Pin" })
@Html.HiddenFor(model => model.PinLength)
<div class="pinpad">
<div class="pinpad-input-row">
<div class="centr">
@for (int i = 0; i < Model.PinLength; i++)
{
<input type="password" id="@(Model.Prefix)-@i" readonly="readonly" class="pinpad-input"/>
}
</div>
</div>
<div class="additional-row">
</div>
<div class="pinpad-keyboard">
<div class="centr">
<div class="pinpad-keys">
<div class="pinpad-key-row">
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="1"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="2"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="3"/>
</div>
<div class="pinpad-key-row">
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="4"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="5"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="6"/>
</div>
<div class="pinpad-key-row">
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="7"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="8"/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="9"/>
</div>
<div class="pinpad-key-row">
<input type="button" class="pinpad-button" value=""/>
<input type="button" class="pinpad-button @(Model.Prefix)val" data-role="button" onclick="pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;" value="0"/>
<a href="#" class="pinpad-button @(Model.Prefix)remVal" data-role="button" onclick="pinpadRemoveLast(@(Model.Prefix)Index,'@(Model.Prefix)');return false;">
<img src="@Url.Content("~/Content/Images/delete.png")" alt="" />
</a>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
1:
2: var @(Model.Prefix)Index = {count:0};
3: if(document.ontouchstart !== undefined || document.ontouchstart == null){
4: $(".@(Model.Prefix)val").bind("touchstart","pinpadPress(this,@(Model.Prefix)Index,'@(Model.Prefix)',@(Model.PinLength));return false;");
5: $(".@(Model.Prefix)val").unbind("click");
6: $(".@(Model.Prefix)remVal").bind("touchstart","pinpadRemoveLast(@(Model.Prefix)Index,'@(Model.Prefix)');return false;");
7: $(".@(Model.Prefix)remVal").unbind("click");
8: }
</script>
Finally, I created a JavaScript file to hold the common functions of the PIN Pad.
function pinpadPress(control, counter, prefix, limit) {
var ctrl = document.getElementById(prefix + "-" + counter.count);
ctrl.value = control.value;
counter.count++;
if (counter.count == limit) pinComplete(prefix, limit);
}
function pinpadRemoveLast(counter, prefix) {
if (counter.count == 0)
return;
counter.count--;
document.getElementById(prefix + "-" + counter.count).value = "";
}
function pinComplete(prefix, limit) {
var pin = "";
for (i = 0; i < limit; i++)
pin += document.getElementById(prefix + "-" + i).value;
document.getElementById(prefix + "Pin").value = pin;
var fctn = prefix + "NextAction";
if (window[fctn]) window[fctn]();
}
After creating these resources, I was able to define and implement the 3 PIN Pads I needed on different Views and Partial Views very easily.
@Html.EditorFor(model=>model.PinPad)