Thursday, May 26, 2011

New jQuery Plugin: FunkyFocus


So this jQuery plugin was built to help my end-users know immediately what section of a form they’re working on. Also, don’t blame me for the name. It was given to my new plugin by my wife because I couldn’t think of anything “catchy”. Basically what this thing does is changes the background of sections of forms or individual inputs. It’ll be better to post screenshots to get the idea…so…here you go…

image

So as soon as you tab to the password textbox, you get this:

image

Here’s how you use it:

  1. Add a reference to jQuery
  2. Add a reference to the jquery.funkyfocus.js
  3. Add this line afterward: $('form').funkyFocus();

If you want to customize how you use it, you have some options. Here they are:

  • class – default is selected
  • sectionOnly – default is true
  • selectorOverride – default is form#{id} input,form#{id} select,form#{id} textarea

If you don’t want to use selected as your class, you’d use this code:

    $('form').funkyFocus({class: 'yourclassname'});

If you don’t want to change the background, but want to change the individual input, do this:

    $('form').funkyFocus({sectionOnly: false});

It would look something like this (assuming you changed the selected style to have a green background):

image

Here’s the actual plugin code:

(function($) {
    $.fn.funkyFocus = function(options) {
  var defaults = {
    class: 'selected',
    sectionOnly: true,
    selectorOverride: 'form#{id} input,form#{id} select,form#{id} textarea'
  };
  var options = $.extend(defaults, options);
  return this.each(function() {
    var selector = options.selectorOverride.replace('{id}', this.id);
    $(selector).focus(function() {
      if (options.sectionOnly)
        $(this).closest("div").addClass(options.class);
      else
        $(this).addClass(options.class);
    });
    $(selector).blur(function() {
      if (options.sectionOnly)
        $(this).closest("div").removeClass(options.class);
      else
        $(this).removeClass(options.class);
    });
        });
    };
})(jQuery);

If you’d like to know how to create a jQuery plugin, check out this post.

Download the plugin here.

Check out the demo here.

As always, please let me know if there’s a way to improve it. Thanks for reading!

Shout it

kick it on DotNetKicks.com

Sunday, May 22, 2011

Contact Form Revisited with ASP.NET MVC 3, jQuery Validator, & the jQuery Form Plugin




I recently added a partial contact view to my MVC3 project and thought I’d share since I did basically this same post over a year ago with the original MVC. I stopped using the Castle Validation because I’ve found that the MVC3 stuff is working for me now. I also am not using fluentHtml anymore because MVC3 uses that style now.

Okay…let’s get started.

We’ll start with the view model class like the last post. By the way, if you’re not familiar with the way I setup my MVC projects, see this post. (If you read my first Contact Form post, you’re probably experiencing déjà vu).

public class ContactView
{
[Required]
public string Name { get; set; }
[Required, ValidateEmail(ErrorMessage = "Valid email is required.")]
public string Email { get; set; }
[Required]
public string Subject { get; set; }
[Required]
public string Message { get; set; }
}

The Required attribute is exactly what it seems like and it’s part of the System.ComponentModel.DataAnnotations. The default error message is “The [propertyname] is required.”. If you want to reset it, you can do this: [Required(ErrorMessage = “Whatever I want it to be”)].

The ValidateEmail is a custom validation attribute. I didn’t like the looks of having a RegularExpressionAttribute defined there and since email is such a common thing to validate, I made this one:

public class ValidateEmailAttribute : RegularExpressionAttribute
{
public ValidateEmailAttribute(): base(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
{
}
}

It just inherits from the RegularExpressionAttribute and passes in the regex to validate an email address. Apparently there is much discussion on how to validate an email address, which I believe is why Microsoft didn’t provide this to us.

So let’s look at the View/XHTML Markup.

@model Sample.Core.UI.Model.ContactView
@using Sample.Core.UI.Helpers
@using (Html.BeginForm("Contact", "Home", FormMethod.Post, new { id = "contactform" }))
{
<fieldset>
<legend>Contact Us</legend>
    <p>
@Html.LabelFor(f => f.Name, "Your Name")<br />
@Html.TextBoxFor(f => f.Name, new { style = "width: 200px", @class="required", accesskey="n" })
</p>
<p>
@Html.LabelFor(f => f.Email, "Your Email")<br />
@Html.TextBoxFor(f => f.Email, new { style = "width: 200px", accesskey = "e" })
</p>
<p>
@Html.LabelFor(f => f.Subject, "Subject")<br />
@Html.TextBoxFor(f => f.Subject, new { style = "width: 200px", accesskey = "p" })
</p>
    <p>
@Html.LabelFor(f => f.Message, "Message")<br />
@Html.TextAreaFor(f => f.Message, new { style = "width: 350px", rows = "4", accesskey = "c" })
</p>
@Html.AntiForgeryToken()
<input type="submit" id="bContact" name="bContact" value="Send Message" accesskey="s" /> 
@Html.DivSuccessMessage("Message sent successfully", "contactsuccess") 
@Html.ValidationSummary("", new { id = "contacterror" })
<noscript><br /><br /><div class="tip">Our contact form may look and act funny because you have JavaScript disabled. For a better experience on thissample.com, please enable JavaScript.</div></noscript>
</fieldset>
}

You can see at the top of the page I specify my view model. I ‘m also referencing a helper namespace for my DivSuccessMessage extension. Basically all it does is checks the ModelState for errors and for ViewData[“success”] not being null and displays the message specified. After that it’s basically a plain ole HTML form.

Okay, now we have our form built with our view model. Below the end of the XHTML above, I have the following jQuery code.

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.8/jquery.validate.min.js"></script>
<script type="text/javascript" src="http://www.malsup.com/jquery/form/jquery.form.js"></script><script language="javascript" type="text/javascript">
$(document).ready(function () {
var formoptions = { beforeSubmit: function (formData, jqForm, options) {
$("#bContact").attr('value', 'sending...');
$("#bContact").attr('disabled', 'disabled');
}, success: function (data) {
if (data.status == "Success") {
$("#contacterror").hide();
showMessage("#contactsuccess", data.message);
$('.valid').removeClass('valid');
validator.resetForm();
}
else {
$("#contactsuccess").hide();
showMessage("#contacterror", data.message);
}
$("#bContact").attr('value', 'Send Message');
$("#bContact").removeAttr('disabled');
}, dataType: "json"
};jQuery.validator.messages.required = "";
var validator = $("#contactform").validate({
submitHandler: function (form) {
$(form).ajaxSubmit(formoptions);
},
invalidHandler: function (e, validator) {
var errors = validator.numberOfInvalids();
if (errors) {
var message = errors == 1 ? 'You missed 1 field. It has been highlighted.' : 'You missed ' + errors + ' fields.  They have been highlighted.';
showMessage("#contacterror", message);
$("#contactsuccess").hide();
} else {
$("#contacterror").hide();
}
},
messages: { Email: { email: ""} },
rules: {
Subject: "required",
Message: "required",
Email: { required: true, email: true }
},
errorClass: "invalid",
validClass: "valid",
errorContainer: "#contacterror"
});function showMessage(id, message) {
$(id).html(message);
$(id).show();
}
});
</script>

Let’s break this down. First section the formOptions:

var formoptions = { beforeSubmit: function (formData, jqForm, options) {
$("#bContact").attr('value', 'sending...');
$("#bContact").attr('disabled', 'disabled');
}, success: function (data) {
if (data.status == "Success") {
$("#contacterror").hide();
showMessage("#contactsuccess", data.message);
$('.valid').removeClass('valid');
validator.resetForm();
}
else {
$("#contactsuccess").hide();
showMessage("#contacterror", data.message);
}
$("#bContact").attr('value', 'Send Message');
$("#bContact").removeAttr('disabled');
}, dataType: "json"
};

This code is used to define all my options for the jQuery.Form plugin. What it says is this:

  • beforeSubmit – Change the button to say “sending…” and disable it
  • on success – if the status = “Success” then hide the contacterror div, show the success message, manually remove the valid class from my inputs, and reset the form. Otherwise, hide the success message and show the error message with the message received. Regardless, re-enable my button and make it say “Send Message”. (Note: I shouldn’t have to manually remove the valid class, but the resetForm wouldn’t do it for me like it’s supposed to do.)
  • dataType – json received from my action

The validation section:

jQuery.validator.messages.required = "";
var validator = $("#contactform").validate({
submitHandler: function (form) {
$(form).ajaxSubmit(formoptions);
},
invalidHandler: function (e, validator) {
var errors = validator.numberOfInvalids();
if (errors) {
var message = errors == 1 ? 'You missed 1 field. It has been highlighted.' : 'You missed ' + errors + ' fields.  They have been highlighted.';
showMessage("#contacterror", message);
$("#contactsuccess").hide();
} else {
$("#contacterror").hide();
}
},
messages: { Email: { email: ""} },
rules: {
Subject: "required",
Message: "required",
Email: { required: true, email: true }
},
errorClass: "invalid",
validClass: "valid",
errorContainer: "#contacterror"
});

This code defines my validation for the contact form. It says this:

  • Set all messages for required fields to empty by default
  • submitHandler – on submit do this, which it calls the ajaxSubmit contained in the jQuery.Form plugin with our options specified above.
  • invalidHandler – if the form isn’t valid, get the number of errors and show the error message. Otherwise, hide the error message.
  • messages – Defines what the message should be for email, which is empty. I would’ve had to specify the required messages too had I not set them to empty first. Also note that the Email: has to match the ID of one of  your inputs.
    • Example: <input type=”text” id=”whateverid”/> so the messages would looks like this:

      messages: {whateverid: {required: “some message”}}

  • rules – Defines the rules for each input. Phone, Comments are required and Email has required and email. Notice name is required, but not specified here. It’s because I added the required class to the Name input in the XHTML instead of specifying down here so you could see you have options.
  • errorClass – Specifies my style class for when the input is invalid.
  • validClass – Specifies my style class for when the input is valid.
  • errorContainer – Specifies the div I want to show my error messages in.

Final section:

function showMessage(id, message) {
$(id).html(message);
$(id).show();
}

This just finds the container sets the html and shows it.

Okay, so finally here’s what the controller looks like that we mentioned in the @Html.BeginForm() section above.

[HttpPost, ValidateAntiForgeryToken, ValidateInput(true)]
public ActionResult Contact(ContactView view)
{
if (!ModelState.IsValid)
{
if (Request.IsAjaxRequest())
return Json(new { status = "error", message = "All fields are required." });

return View(view);
}    try
{
var notificationService = DI.EmailNotificationService(new EmailNotification(view));
notificationService.Notify();
}
catch (NotificationException)
{
ModelState.AddModelError("notifyerror", "Could not connect to mail server.");
}    if (Request.IsAjaxRequest())
return ModelState.IsValid ? Json(new {status = "Success", message = "Message sent successfully."}) : Json(new {status = "error", message = "Could not connect to mail server."});    return ModelState.IsValid ? Success(view) : View(view);
}

So this action accepts HttpPost, must have a valid Anti-Forgery Token, and it validates the input. First thing it does is verifies the modelstate is valid. The reason for this is that some people run their browser with JavaScript disabled. So we have to account for that in our code and make sure that we are validating on the client-side and on the server-side. So if the ModelState is invalid, we have to check to see if it’s an AJAX request. if it is, we return a Json result with the status of error and a message stating all fields are required. If it’s not an AJAX request, we simply return the view.

If all is valid, we continue and go ahead and send the notification. If the notification bombs, we add an error to the modelstate and then recheck and act accordingly. If you want to know what the notification service looks like, please refer to the first post because it’s all the exact same.

So, this method of coding will work when JavaScript is enabled and disabled and all the data will be validated regardless as well.

Here’s what the screen looks like after just hitting Send Message:

image

Here’s what it looks like after all the fields are valid right before I hit Send Message:

image

After message sent:

image

Please let me know if you have any questions.

Download Demo

Thanks for reading!

Shout it

kick it on DotNetKicks.com

Monday, May 09, 2011

Seriously WebForms like ASP.NET MVC




I used my little WebFormContrib library again today. Some days I love revisiting old code because you realize how ignorant of some practices you were in the past. Hopefully none of you download it and say…geez this guy is Mr. Ignoramus. If you do, keep it to yourself. Kidding, please comment and inform the ignorant (me).

Anyhow, it’s been about 6 months since I’ve had to use WebForms, but today I had to and it wasn’t bad. I was able to tie my view into my pages and controls and used AutoMapper to map back to the domain from it. To me, WebFormContrib makes WebForms kinda fun again…cause it makes it seem new. I really do think it’s a great stepping stone to using MVC just because you kinda get used to the syntax. I also think it’s a decent library because I didn’t have to go relearn how it worked, I just referenced the library and then started working on my little WebForm app. I set it up just like I do my MVC apps. I also had to add a couple things and it was easily extendable, which I’m sure you all know is a good thing.

Anyhow, if you have no idea what I’m talking about, please read my previous posts on WebFormContrib.

Also, how could I post without a code sample? In “The Original” post, I mentioned the first thing I wanted to refactor was the validation section. Well, I did. Here’s the new and improved ModelIsValid() method:

        internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var value = property.GetValue(view, null);

var attributes = property.GetCustomAttributes(typeof(IValidationAttribute), false);
foreach (IValidationAttribute valatt in attributes)
if (!valatt.IsValid(value))
ErrorMessages.Add(valatt.Message);
}

return ErrorMessages.Count == 0;
}

Previously, it looked like this:

        internal bool ModelIsValid(TModel view)
{
ErrorMessages =
new List<string>();
foreach (var property in typeof(TModel).GetProperties())
{
var attributes = property.GetCustomAttributes(typeof(RequiredAttribute), false);
if (attributes.Length > 0)
{
var value = property.GetValue(view, null);
if (value.ToSafeString() == string.Empty)
ErrorMessages.Add(((
RequiredAttribute)attributes[0]).Message);
}
}

if (ErrorMessages.Count == 0)
return true;

return false;
}

Obviously…I was a moron. I still don’t think it’s perfect, but it is a serious improvement. It’s funny how you don’t see how to refactor something until you need to extend it. As soon as I created the length validator and added the code here I thought…wow this is dumb and then OH! do it this way. Anyhow, I thought I’d share the slight improvement.

You can download the source and samples here…or just download the DLL here.

Thanks for reading!

Shout it

kick it on DotNetKicks.com

Related Posts Plugin for WordPress, Blogger...