We help IT Professionals succeed at work.

jQuery Javascript adds an extra row to the table in MVC

jwebster77
jwebster77 used Ask the Experts™
on
Hi,
I have a main view which has a "Comments" link on each row that when clicked it opens up a partial view. The partial view displays a table and textboxes, and an "Add" button that when clicked it adds the data entered in the textboxes to the table.

The first time I click on comments, I add data to the textboxes and hit the Add button and everything works.

The problem is:  when I open the partial view from another record(by clicking on Comments) and add data to the textboxes, after I click on add it adds an extra blank row.  It is as if the add button is clicked twice.  Could you please help me solve this issue?  The code I am using is below.
Thank you very much.

**PARTIAL VIEW:**
        @model IEnumerable<HelpDeskSupport.Models.Comment>

        <html>
        <body>
    <table class="table table-striped table-bordered" cellpadding="0" cellspacing="0" border="0" width="1500" id="tblComments">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.TicketNumber)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Comment1)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.AssignedTo)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.CreatedBy)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Date)
                </th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.TicketNumber)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Comment1)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.AssignedTo)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.CreatedBy)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Date)
                    </td>
                </tr>
            }
            
        </tbody>
        <tfoot>
            <tr>
                <td><input type="text" id="txtTicketNumber" value=@ViewData["NumberFromViewAll"] readonly /></td>
                <td><input type="text" id="txtComment" /></td>
                <td><input type="text" id="txtAssignedTo" /></td>
                <td><input type="text" id="txtCreatedBy" /></td>
                <td><input type="text" id="txtDate" /></td>
                <td><input type="button" id="btnAddComment" value="Add" /></td>
            </tr>
        </tfoot>
    </table>
    <br />
    <input type="button" id="btnSave" value="Save All" />

    @*<script type="text/javascript" src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>*@
    <script src="~/Scripts/json2.js"></script>
    <script type="text/javascript">
        $("body").on("click", "#btnAddComment", function () {
            //Reference the TextBoxes
            var txtTicketNumber = $("#txtTicketNumber");
            var txtComment = $("#txtComment");
            var txtAssignedTo = $("#txtAssignedTo");
            var txtCreatedBy = $("#txtCreatedBy");
            var txtDate = $("#txtDate");

            //Get the reference of the Table's TBODY element
            var tableBody = $("#tblComments > TBODY")[0];
            //Add Row
            var row = tableBody.insertRow(-1);

            //Add TicketNumber cell          
            var cell = $(row.insertCell(-1));
            cell.html(txtTicketNumber.val());

            //Add Comment cell
            cell = $(row.insertCell(-1));
            cell.html(txtComment.val());

            //Add AssignedTo cell
            cell = $(row.insertCell(-1));
            cell.html(txtAssignedTo.val());

            //Add CreatedBy cell
            cell = $(row.insertCell(-1));
            cell.html(txtCreatedBy.val());

            //Add Date cell
            cell = $(row.insertCell(-1));
            cell.html(txtDate.val());           

            //Clear the TextBoxes
            txtComment.val("");
            txtAssignedTo.val("");
            txtCreatedBy.val("");
            txtDate.val("");
        });

        $("body").on("click", "#btnSave", function () {
            //Loop through the Table rows and build a JSON array
            var commentsArray = new Array();
            $("#tblComments TBODY TR").each(function () {
                var row = $(this);
                var commentLine = {};
                commentLine.TicketNumber = row.find("TD").eq(0).html();
                commentLine.Comment1 = row.find("TD").eq(1).html();
                commentLine.AssignedTo = row.find("TD").eq(2).html();
                commentLine.CreatedBy = row.find("TD").eq(3).html();
                commentLine.Date = row.find("TD").eq(4).html();
                commentsArray.push(commentLine);
            });

            //Send the JSON array to Controller using AJAX
            $.ajax({
                type: "POST",
                url: "/Tickets/InsertComments",
                data: JSON.stringify(commentsArray),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function () {
                    alert("Comment(s) inserted.");
                }
            });
        });
    </script>
</body>
</html>

Open in new window



**CONTROLLER:**
         //VIEW ALL COMMENTS AND DISPLAY IN PARTIAL
        [HttpPost]
        public ActionResult ViewComments(int ticketNum)
        {
            List<Comment> AllComments = new List<Comment>();
            using (DBModel db = new DBModel())
                AllComments = db.Comments.Where(x => x.TicketNumber == ticketNum).ToList();

            //get iNumber from ViewAll to display in the ticket number textbox of the comments partial view
            ViewData["NumberFromViewAll"] = ticketNum;

            return PartialView("ViewComments", AllComments);
        }

        public JsonResult InsertComments(List<Comment> commentsArray)
        {
            using (DBModel db = new DBModel())
            {
                if (commentsArray != null)
                {
                    var lastColumnComments = commentsArray.Last();
                    var ticketNumberToDelete = lastColumnComments.TicketNumber;
                    var sqlQuery = "Delete [Comments] where TicketNumber = " + ticketNumberToDelete;
                    //Delete all comments from comments table. Previous comments are copied in javascript and re-populated
                    db.Database.ExecuteSqlCommand(sqlQuery);
                }

                //Check for NULL
                //if (commentsArray == null)
                //{
                //    commentsArray = new List<Comment>();
                //}

                foreach (Comment c in commentsArray)
                {
                    db.Comments.Add(c);
                }               
                int insertedRecords = db.SaveChanges();
                return Json(insertedRecords);
            }
        }

Open in new window



**IN CASE YOU WANT TO SEE HOW THE VIEWCOMMENTS IS DISPLAYED FROM THE MAIN VIEW:**
      @section modalComments
    {
    <script type="text/javascript">
        function showComments() {
            $("#dialog").dialog({
                autoOpen: false,
                modal: true,
                title: "View Details"
            });
            $("#ticketTable .details").click(function () {
                var ticketNum = $(this).closest("tr").find("td").eq(0).html();
                $.ajax({
                    type: "POST",
                    url: "/Tickets/ViewComments",
                    data: '{ticketNum: "' + ticketNum + '" }',
                    contentType: "application/json; charset=utf-8",
                    dataType: "html",
                    success: function (response) {
                        $('#dialog').html(response);
                        $('#dialog').dialog('open');
                    },
                    failure: function (response) {
                        alert(response.responseText);
                    },
                    error: function (response) {
                        alert(response.responseText);
                    }
                });
            });
        };

        $(function () {
            showComments();
        });   

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
leakim971Multitechnician
Top Expert 2014

Commented:
remove the javascript from your partial view and add it to your main page AT LEAST to test

Author

Commented:
I tried that and the page does not work as the javascript calls for buttons and textboxes that are on the partial view.
The functionality works and it adds rows and then save them to the database.  The problem comes when you hit on another comments link(so the second time I hit a Comments link) and try to add something:  it adds extra rows when clicking on the add button.  Then if you go again to another "Comments" link and click on Add it adds that extra row and another one, and so on every time I click on Add.

Author

Commented:
By debugging it, it looks like the add button runs twice, but that happens not the first time around instead when I click on another Comments link and try to add.  So it is for the save all button as it saves the same thing to the database twice.  Could anyone suggest what code changes I should make?  I cannot seem to see the error.  Thank you very much.
Multitechnician
Top Expert 2014
Commented:
I tried that and the page does not work

probably because you have no body yet (the page not fully loaded)

so replace add the whole code in a $(document).ready(function() { .your code here }); IN THE MAIN PAGE :

<script type="text/javascript">
    jQuery(function($) { // SHORTCUT OF $(document).ready(function($) {
        $(document).on("click", "#btnAddComment", function () {
            //Reference the TextBoxes
            var txtTicketNumber = $("#txtTicketNumber");
            var txtComment = $("#txtComment");
            var txtAssignedTo = $("#txtAssignedTo");
            var txtCreatedBy = $("#txtCreatedBy");
            var txtDate = $("#txtDate");

            //Get the reference of the Table's TBODY element
            var tableBody = $("#tblComments > TBODY")[0];
            //Add Row
            var row = tableBody.insertRow(-1);

            //Add TicketNumber cell          
            var cell = $(row.insertCell(-1));
            cell.html(txtTicketNumber.val());

            //Add Comment cell
            cell = $(row.insertCell(-1));
            cell.html(txtComment.val());

            //Add AssignedTo cell
            cell = $(row.insertCell(-1));
            cell.html(txtAssignedTo.val());

            //Add CreatedBy cell
            cell = $(row.insertCell(-1));
            cell.html(txtCreatedBy.val());

            //Add Date cell
            cell = $(row.insertCell(-1));
            cell.html(txtDate.val());

            //Clear the TextBoxes
            txtComment.val("");
            txtAssignedTo.val("");
            txtCreatedBy.val("");
            txtDate.val("");
        });

        $(document).on("click", "#btnSave", function () {
            //Loop through the Table rows and build a JSON array
            var commentsArray = new Array();
            $("#tblComments TBODY TR").each(function () {
                var row = $(this);
                var commentLine = {};
                commentLine.TicketNumber = row.find("TD").eq(0).html();
                commentLine.Comment1 = row.find("TD").eq(1).html();
                commentLine.AssignedTo = row.find("TD").eq(2).html();
                commentLine.CreatedBy = row.find("TD").eq(3).html();
                commentLine.Date = row.find("TD").eq(4).html();
                commentsArray.push(commentLine);
            });

            //Send the JSON array to Controller using AJAX
            $.ajax({
                type: "POST",
                url: "/Tickets/InsertComments",
                data: JSON.stringify(commentsArray),
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function () {
                    alert("Comment(s) inserted.");
                }
            });
        });
    });
</script>

Open in new window

leakim971Multitechnician
Top Expert 2014

Commented:
By debugging it, it looks like the add button runs twice

this is my point asking you to put it in the main page, else you declare everytime you call the partial view new events thresholds (add and save)

Author

Commented:
I did what you suggested ("replace add the whole code in a $(document).ready(function()")  and now the Add button is not being fired at all.  Here is the code I have now for the main view and the partial view.  I did not change anything in the controller.

MAIN VIEW:

@model IEnumerable<HelpDeskSupport.Models.Ticket>

@{
    ViewBag.Title = "ViewAll";
    //Layout = null;
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css">                @*for the visual style of the datepicker of the dtLastUpdated column*@


@section modalComments
    {
    <script type="text/javascript">
        function showComments() {
            $("#dialog").dialog({
                autoOpen: false,
                modal: true,
                title: "View Details"
            });
            debugger;
            $("#ticketTable .details").click(function () {
                var ticketNum = $(this).closest("tr").find("td").eq(0).html();
                $.ajax({
                    type: "POST",
                    url: "/Tickets/ViewComments",
                    data: '{ticketNum: "' + ticketNum + '" }',
                    contentType: "application/json; charset=utf-8",
                    dataType: "html",
                    success: function (response) {
                        $('#dialog').html(response);
                        $('#dialog').dialog('open');
                    },
                    failure: function (response) {
                        alert(response.responseText);
                    },
                    error: function (response) {
                        alert(response.responseText);
                    }
                });
            });
        };

        $(function () {
            showComments();
        });
    </script>
}

<head>
<script src="~/Scripts/json2.js"></script>
<script type="text/javascript">
    jQuery(function ($) {                                           // SHORTCUT OF $(document).ready(function($) {
            $(document).on("click", "#btnAddComment", function () {
                debugger;
                //Reference the TextBoxes
                var txtTicketNumber = $("#txtTicketNumber");
                var txtComment = $("#txtComment");
                var txtAssignedTo = $("#txtAssignedTo");
                var txtCreatedBy = $("#txtCreatedBy");
                var txtDate = $("#txtDate");

                //Get the reference of the Table's TBODY element
                var tableBody = $("#tblComments > TBODY")[0];
                //var tBody = $("#tblComments")[0];

                //Add Row
                var row = tableBody.insertRow(-1);

                //Add TicketNumber cell
                var cell = $(row.insertCell(-1));
                cell.html(txtTicketNumber.val());

                //Add Comment cell
                cell = $(row.insertCell(-1));
                cell.html(txtComment.val());

                //Add AssignedTo cell
                cell = $(row.insertCell(-1));
                cell.html(txtAssignedTo.val());

                //Add CreatedBy cell
                cell = $(row.insertCell(-1));
                cell.html(txtCreatedBy.val());

                //Add Date cell
                cell = $(row.insertCell(-1));
                cell.html(txtDate.val());

                 //Clear the TextBoxes
                txtComment.val("");
                txtAssignedTo.val("");
                txtCreatedBy.val("");
                txtDate.val("");
            });

                $(document).on("click", "#btnSave", function () {
                //Loop through the Table rows and build a JSON array
                    var commentsArray = new Array();
                    $("#tblComments TBODY TR").each(function () {
                        //$("#tblComments TBODY TR").last(function () {
                        var row = $(this);
                        var commentLine = {};
                        commentLine.TicketNumber = row.find("TD").eq(0).html();
                        commentLine.Comment1 = row.find("TD").eq(1).html();
                        commentLine.AssignedTo = row.find("TD").eq(2).html();
                        commentLine.CreatedBy = row.find("TD").eq(3).html();
                        commentLine.Date = row.find("TD").eq(4).html();
                        commentsArray.push(commentLine);
                    });
                debugger;
                //Send the JSON array to Controller using AJAX
                    $.ajax({
                        type: "POST",
                        url: "/Tickets/InsertComments",
                        data: JSON.stringify(commentsArray),
                        contentType: "application/json; charset=utf-8",
                        dataType: "json",
                        success: function () {
                            alert("Comment(s) inserted.");
                        }
                    });
                });
    });
</script>
</head>


<button type="button" onclick="cleanFilters()" value="Clear Filters" id="filterBtn" class="btn btn-primary btn-sm">Clear Filters / Reset</button>


@*jQuery Datatable (and could also be for ThemeRoller*@
<table class="display table table-striped table-bordered" id="ticketTable" style="width:100%">
    <thead>
        <tr id="filters">
            <th>
                @Html.DisplayNameFor(model => model.iNumber)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.dtLastUpdated)
                <input type="text" id="datepicker" class="datepicker">    @*readonly="readonly"*@
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcSubject)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcFrom)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcPriority)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcAssignedTo)
            <th>
                @Html.DisplayNameFor(model => model.vcStatus)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcRequestType)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcLocation)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.vcCategory)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.dtAnticipatedCompletion)
            </th>
            <th></th>
        </tr>
    </thead>

    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.iNumber)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.dtLastUpdated)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcSubject)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcFrom)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcPriority)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcAssignedTo)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcStatus)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcRequestType)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcLocation)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.vcCategory)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.dtAnticipatedCompletion)
                </td>
                <td>

                    <a class="btn btn-primary btn-sm" onclick="Edit('@Url.Action("EditOneRecord", "Tickets", new { id = item.iNumber })')"><i class="fa fa-pencil fa-lg"></i></a>
                    <a class="btn btn-danger btn-sm" onclick="Delete('@Url.Action("Delete", "Tickets", new { id = item.iNumber })')"><i class="fa fa-trash fa-lg"></i></a>
                    <a class="details" href="javascript:;">Comments</a>
                </td>
            </tr>
        }
    </tbody>
</table>


<div id="dialog" style="display: none">
</div>

Open in new window



PARTIAL VIEW:

@model IEnumerable<HelpDeskSupport.Models.Comment>

<html>
<body>
    <table class="table table-striped table-bordered" cellpadding="0" cellspacing="0" border="0" width="1500" id="tblComments">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.TicketNumber)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Comment1)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.AssignedTo)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.CreatedBy)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Date)
                </th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.TicketNumber)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Comment1)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.AssignedTo)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.CreatedBy)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Date)
                    </td>
                </tr>
            }
        </tbody>
        <tfoot>
            <tr>
                <td><input type="text" id="txtTicketNumber" value=@ViewData["NumberFromViewAll"] readonly /></td>
                <td><input type="text" id="txtComment" /></td>
                <td><input type="text" id="txtAssignedTo" /></td>
                <td><input type="text" id="txtCreatedBy" /></td>
                <td><input type="text" id="txtDate" /></td>
                <td><input type="button" id="btnAddComment" value="Add" /></td>
            </tr>
        </tfoot>
    </table>
    <br />
    <input type="button" id="btnSave" value="Save All" />

</body>
</html>

Open in new window

leakim971Multitechnician
Top Expert 2014

Commented:
do a right click on the add button when you've the partial view loaded in the browser/page
and check the ID attribute
it should match to "btnAddComment" else let me know
leakim971Multitechnician
Top Expert 2014

Commented:
you need to put this script after the declaration(script tag) of the jQuery plugin

Author

Commented:
Hi, I think it is working now.  Does that mean that I have to leave your code in the main view and not the partial view? Why would that be?

Anyway, after adding your code I forgot to check the console where I noticed it was missing a jQuery library.  I added that and now it seems to work.  Thank you so much!  I will make sure tomorrow still works fine as I am leaving now and then if it is I will mark this as the solution.  Thanks again
leakim971Multitechnician
Top Expert 2014

Commented:
Does that mean that I have to leave your code in the main view and not the partial view? Why would that be?

You should until you've good reason to not put it there

Author

Commented:
Thank you very much!