MVC Create custom comment system in c# with javascript

by John Nye

29 Nov

I recently created a custom blog engine for this blog and thought I'd share a simple overview of how I did it.

Demo: Simple validation demo to show custom comment validation

Creating the data models

In order to persist our comments in a data source, and retrieve them back, we need to define a data model that defines the information we require:

public class Comment
    public int Id { get; set; }

    public string Author { get; set; }

    public string Body { get; set; }

    public string Email { get; set; }

    public int PostId { get; set; }

    // Link to existing Post class 
    public virtual Post Post { get; set; }

I want to be able to access comments from a post so I also need to update my post model as follows:

public class Post
    // Current Properties...

    // New relationship property
    public virtual ICollection<Comment> Comments { get; set; }

Now I have my data models I simply need to create a data migration and update my database using the following commands

Add-Migration CreateComments -ProjectName Blog.Data -Verbose Update-Database -ProjectName Blog.Data -Verbose

Now that our database is up to date you may need to implement some code in order to access your data, be it a repository or otherwise.

Creating the view model

Now that the data can be persisted and retrieved from our datasource we can concentrate on how we allow users to create a comment. Firstly we need to create a viewmodel to represent a comment:

public class CommentViewModel
    //Represents a post id
    public int Id { get; set; }
    public string Author { get; set; }
    public string Body { get; set; }
    public string Email { get; set; }

Creating the HttpPost action

We also need to create a post action in our controller to accept the comment form submissions. This will be a simple MVC post action like any other:

public ActionResult Comment(CommentViewModel viewModel)
    if (ModelState.IsValid)
        //Mapping code - alternatively try AutoMapper
        var dataComment = new Comment();
        dataComment.PostId = viewModel.Id;
        dataComment.Author = viewModel.Author;
        dataComment.Body = viewModel.Body;
        dataComment.Email = viewModel.Email;

        // Create comment and save changes

        return new EmptyResult();

    var modelErrors = this.BuildModelErrors();
    // return a bad request to signify that adding the comment failed
    HttpContext.Response.StatusCode = 400;
    // return errors as Json, read by javascript
    return Json(modelErrors);

/// <summary>
/// Build a list of model errors from model state.  
/// This method flattens the model state errors.
/// </summary>
/// <returns>A list of Keys and Error messages</returns>
private List<ModelError> BuildModelErrors()
    var modelErrors = new List<ModelError>();
    var erroneousFields = this.ModelState.Where(ms => ms.Value.Errors.Any())
                                         .Select(x => new {x.Key, x.Value.Errors});

    foreach (var erroneousField in erroneousFields)
        var fieldKey = erroneousField.Key;
        var fieldErrors = erroneousField.Errors.Select(error => 
                                            new ModelError(fieldKey, error.ErrorMessage));
    return modelErrors;

//Class to hold model errors and the corresponding field key
private class ModelError
    public ModelError(string key, string errorMessage)
        Key = key;
        ErrorMessage = errorMessage;

    public string Key { get; set; }
    public string ErrorMessage { get; set; }

If the view model fails validation I return a HttpStatusCodeResult with a HttpStatusCode.BadRequest (400) status code. This will ensure that our response is not treated as a success in our javascript, but more on that further down

Creating the form

We now need to create a form that can be used to create a comment. I have created this as a partial view so that I can add it easily to multiple pages

I could have simply created a simple Html.BeginForm() or Ajax.BeginForm() but I wanted a custom implementation with cleaner html so I decided to use plain html and javascript with a lot of help from jquery.

@model Blog.Models.Comments.CommentViewModel

<div class="comment-form-container">
    <form class="comment-form" data-action="@Url.Action("Comment", "Comments")">
        @Html.HiddenFor(m => m.Id)
        <div class="comment-body">
            <div>@Html.LabelFor(m => m.Author)</div>
            @Html.TextBoxFor(m => m.Author, new { Class = "comment-name" })
            <div>@Html.LabelFor(m => m.Email)</div>
            @Html.TextBoxFor(m => m.Email, new { Class = "comment-email" })
            <div>@Html.LabelFor(m => m.Body)</div>
            @Html.TextAreaFor(m => m.Body, new { Class="comment-body", rows="3", cols="50" })
        <div class="comment-result" style="display: none;" >
            <span class="comment-result-text">An error occurred</span>
            <button type="submit" class="comment-form-submit">Submit comment</button>

Now we have a form we simply need to hook up the form submission

The javascript

Below is some javascript that can be used to post our form to the controller in order to submit a form:

$('.comment-form-container').on('click', '.comment-form-submit', function(e) {
    // prevent form submission
    var form = $(this).closest('form');
    var resultMessage = $('.comment-result', form);

    var submitButton = $(this);
    // disable the submit button to stop 
    // accidental double click submission
    submitButton.attr('disabled', 'disabled');
    var resultMessageText = $('.comment-result-text', form);

    // client side validation
    if (validateComment(form, resultMessageText) == false) {
        // Re-enable the submit button

    var postUrl ='action');
    var postData = form.serialize();

    $.post(postUrl, postData)
        .done(function(data) {
            resultMessageText.html('Comment created successfully!');
            // Clear submitted value
            $('.comment-body', form).val('');
        .fail(function() {
            resultMessageText.html('An error has occurred');
        .always(function() {

I have also added some simple validation to check, client side, that required fields have values. This is in place to simply prevent unnecessarily posting back to the server if we already know a required field hasn't been given a value.

// validate required fields
var validateRequired = function (input) {
    if (input.val().length == 0) {
        return false;
    return true;

var validateComment = function (commentForm, resultMessageContainer) {
    var isValid = true;
    var errorText = '';
    var name = $('.comment-name', commentForm);
    if (!validateRequired(name)) {
        errorText += 'The Name field is required.<br />';
        isValid = false;

    var body = $('.comment-body', commentForm);
    if (!validateRequired(body)) {
        errorText += 'The Body field is required.<br />';
        isValid = false;

    var email = $('.comment-email', commentForm);
    if (!validateRequired(email)) {
        errorText += 'The Email field is required.<br />';
        isValid = false;

    if (isValid == false) {

    return isValid;

In the following post I will show how we can leverage jquery validate to perform the client side validation for us automatically to replace our custom validation.

Don't forget to check out the demo

Demo: Simple validation demo to show custom comment validation

Any comments, please let me know using the form below.

Comments 28

Dean says: 3860 days ago

Your comment count is missing a :hover color :-p

Nice blog mate :-)

mukesh says: 3699 days ago

hello john nye, where r u displaying comments after being submitted...?

John says: 3695 days ago

Hi Mukesh,

The Demo page doesn't display the comments as it's purpose is more to demonstrate the validation. If validation is passed and the server receives the comment, how you handle and display the comment is up to you.

The same system is used on this blog and I use the data posted to create a new comment entry instead of receiving new data.

Hope this helps

Brian says: 3648 days ago

Ideally or in today's WWW you want to be able to edit comments and delete comments using AJAX. That is where things can get tricky IMO. Having comments in textbox containers in a forloop was my first attempt, but it is just not clean enough for me. What do you suggest for html and how could I pass the comment's ids for AJAX? Thanks in advance

Brian says: 3648 days ago

Oh, I forgot if anyone is interested in attempting to create commenting section using knockout.js this is a great tutorial but once again uses texbox/textarea

John says: 3646 days ago

Hi Brian,

Editing becomes a bit more tricky but I do currently have the delete functionality on my blog site. Basically, I have created a new action result in controller called Delete

public ActionResult Delete(int id)
    return new EmptyResult();

This coupled with the following javascript

$(function () {
    // Setup on click delete action across all comments
    $(&#39;.blog-comments&#39;).on(&#39;click&#39;, &#39;.delete-comment&#39;, function () {
        // find the related post html
        var post = $(this).closest(&#39;.blog-comment&#39;);
        // Get the post id from the post data attribute
        var postData = { id:;post-id&#39;) };

        // Make a request to delete the post
        $.post(&#39;/posts/delete&#39;, postData)
            .always(function () {
                // Remove the post html from page gracefully
                post.slideUp(function () {

Hope this helps.

Furkan says: 2938 days ago


robin says: 2862 days ago

nice good

Meee says: 2843 days ago


sedat says: 2714 days ago

very good

nvm says: 2618 days ago


Tony says: 2534 days ago


brad says: 2519 days ago

very good

saira says: 2505 days ago


SirLucky says: 2490 days ago

Its actually something like this i want for my site

za says: 2438 days ago


XYz says: 2428 days ago


Bob says: 2425 days ago

<b>thanks</b> for <b>sharing</b>

akber says: 1869 days ago

pretty good work

Parmarvipul says: 1570 days ago


Vikas says: 1434 days ago


ada says: 1196 days ago


eee says: 1088 days ago


NULL says: 1029 days ago


NULL says: 1029 days ago


te says: 1017 days ago


Mahesh says: 647 days ago


mo says: 636 days ago


Leave a message...

24 Jun