Imad
Imad

Reputation: 7490

Ajax in Asp.Net MVC strange behavior

My mark up has one div with id= "divGrid" and one ajax enabled form outside that div. Form contains two text inputs and one submit button with UpdateTargetId = "divGrid". It does update that grid perfectly but add duplicate ajax-enabled form on the page. Why this? How to fix this?

Here is my View:

@model List<EF.Models.Data.Books>

@{
    ViewBag.Title = "CodeFirst";
    Layout = null;
}
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<div id="divGrid">

        @foreach (var item in Model)
        {
        <table>
            <tr>
                <td>@item.ID</td>
                <td>@item.Name</td>
                <td>@item.Price</td>
                </tr>
            </table>
            }
    </div>
    @using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "divGrid", InsertionMode = InsertionMode.Replace }))
    {
        @Html.TextBox("name");
        <br />
        @Html.TextBox("price");
        <input type="submit" value="Insert" />    
    }

My controller action:

[HttpPost]
    public ViewResult CodeFirst(FormCollection form)
    {
        book.Name = form["name"];
        book.Price = Convert.ToDecimal(form["price"]);
        context.Books.Add(book);
        context.SaveChanges();
        result = context.Books.ToList();
        return View(result);
    }

Interestingly, my rendered mark-up is something like

<div id="divGrid">
    <div id="divGrid">
    <!-- Data display as expected -->
   </div>
   <!-- ajax enabled form, that is out side of this in source code -->
</div>
<!-- ajax enabled form, that is out side of this in source code -->

enter image description here

Upvotes: 0

Views: 53

Answers (1)

user3559349
user3559349

Reputation:

Your CodeFirst() method is returning the same view you initially rendered and its being inserted inside the <div id="divGrid"> which is why you see the form repeated. You need to return a separate partial view containing only the table. Create a separate child only method to display the initial table

[ChildActionOnly]
public ActionResult BookTable()
{
  var model = context.Books; // no need for .ToList()
  return PartialView(model);
}

BookTable.cshtml

@model IEnumerable<EF.Models.Data.Books>
@foreach (var item in Model)
{
  <table>
    <tr>
      <td>@item.ID</td>
      <td>@item.Name</td>
      <td>@item.Price</td>
    </tr>
  </table>
}

Then create a view model for adding a new book so you can take advantage of MVC features such as strong type binding, client and server side validation etc.

public class BookVM
{
  [Required]
  public string Name { get; set; }
  public decimal Price { get; set; }
}

and you controller methods become

public ActionResult CodeFirst()
{
  BookVM model = new BookVM();
  return View(model);
}

[HttpPost]
public ViewResult CodeFirst(BookVM model)
{
    Book book = new Book(){ Name = model.Name, Price = model.Price };
    context.Books.Add(book);
    context.SaveChanges();
    result = context.Books();
    return PartialView("BookTable", result);
}

and change your view to

@model BookVM
....
<div id="divGrid">
  @Html.Action("BookTable");
</div>

@using (Ajax.BeginForm(new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "divGrid", InsertionMode = InsertionMode.Replace }))
{
  @Html.LabelFor(m => m.Name)
  @Html.TextBoxFor(m => m.Name)
  @Html.ValidationMessageFor(m => m.Name)
  .... // ditto for Price
  <input type="submit" value="Insert" />    
}

However this means your regenerating the html for the whole table everytime you submit the form. You would get far better performance by just using jquery and ajax to call a method that inserts the book and returns its new ID as json, then dynamically adding a new row to the table based on the returned ID, and the values from the textboxes in the form.

Upvotes: 1

Related Questions