4

I'm learning MVC3 and building a little "to-do" website as a learning exercise, so I'm open to the idea that I'm just completely going down the wrong path!

Anyway, I have a page working perfectly with regular postbacks. I'm trying to Ajax it up with jQuery and UnobtrusiveAjax and everything still technically works correctly (the data is passed to the controller and saved in my database). The problem is that in the element I replace, each form's fields are all filled with the values that I just passed in on the one form.

Index.cshtml

@model WebUI.Models.HomeViewModel

@{
    ViewBag.Title = "Index";
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
}

<h2>My Goals</h2>
...snip...
<div id="goal_div">
@foreach (var goal in Model.Goals)
{
    Html.RenderPartial("GoalDetail", goal);
}
</div>

GoalDetail.cshtml

@model Domain.Entities.Goal
<div class="goal" id='goal_id@(Model.ID)'>
    <h4>@Model.Name</h4>
    <p>@DateTime.Now.ToString()</p>
    <p class="goal_description">@Model.Progress % complete</p>
    <ul>
        @foreach (var task in Model.Tasks)
        {
            using (Ajax.BeginForm("UpdateTask", "Home", new AjaxOptions { UpdateTargetId = "goal_id" + Model.ID }))
            {
                @Html.HiddenFor(g => g.ID)
                @Html.Hidden("TaskID", task.ID)
                <li class="task">
                    @Html.CheckBox("IsComplete", task.IsComplete) 
                    @Html.TextBox("TaskName", task.Name)
                    @task.Name
                    <input class="actionButtons" type="submit" value="Update task" />
                </li>
            }
        }
        <li>
        @using (Html.BeginForm("AddTask", "Home"))
        {
            @Html.HiddenFor(g => g.ID)
            @Html.Editor("TaskName")
            <input class="actionButtons" type="submit" value="Add task" />
        }
        </li>
    </ul>
</div>

HomeController.cs

public class HomeController : Controller
{
    private IGoalRepository Repository;

    public HomeController(IGoalRepository repo)
    {
        Repository = repo;
    }

    public ViewResult Index()
    {
        HomeViewModel viewModel = new HomeViewModel();
        viewModel.Goals = Repository.Goals;
        return View(viewModel);
    }

    public ActionResult AddTask(int ID, string TaskName)
    {
        bool success = Repository.SaveTask(ID, 0, TaskName, DateTime.Today, false);
        return RedirectToAction("Index");
    }

    public ActionResult UpdateTask(int ID, int TaskID, bool IsComplete, string TaskName)
    {
        bool success = Repository.SaveTask(ID, TaskID, TaskName, DateTime.Today, IsComplete);
        Goal updatedGoal = Repository.Goals.FirstOrDefault(g => g.ID == ID);
        return PartialView("GoalDetail", updatedGoal);
    }

    public ActionResult AddGoal(string Name, DateTime Enddate)
    {
        bool success = Repository.SaveGoal(0, Name, DateTime.Today, Enddate);
        return RedirectToAction("Index");
    }

    public ActionResult UpdateGoal(int GoalID, string Name, DateTime Enddate)
    {
        bool success = Repository.SaveGoal(GoalID, Name, DateTime.Today, Enddate);
        return RedirectToAction("Index");
    }
}

I have the time there just to make sure that the AJAX refresh has actually happened, and you'll see why I have the task name there twice.

This is what I see when I first load the page: Before

Then I check the checkbox of the the 2nd task of the 1st goal, rename it "Updated Task #2", and click the update button. That's when this happens: After

Seeing how the task names NOT part of the form are all correct (ignoring the re-ordering for now), and the progress value has been updated correctly (it just takes the completed tasks and divides by the total number of tasks), I have no idea why all the form values have been replaced. Even the AddTask form has been filled in, even though I haven't changed that one to use Ajax yet. I've been searching for reasons for this for 2 days now, and have come up empty.

Community
  • 1
  • 1
Jason
  • 1,325
  • 6
  • 14
  • I did more research into this, and it appears like most examples using MVC and Ajax are returning JSON objects instead of a partial view. I'm going to try doing that after work today and see if that makes a difference. – Jason Jun 05 '12 at 15:33

1 Answers1

2

After even more searching, I finally discovered the issue. Basically, it has to do with the way ModelState works in MVC. Reading this help thread and this article really helped me understand what was happening with my page. I ended up calling ModelState.Clear() in my controller right before returning the partial view, but this SO question and answer suggests another method.

Community
  • 1
  • 1
Jason
  • 1,325
  • 6
  • 14
  • I have/had a similar issue, and `ModelState.Clear()` was the only thing that seemed to work, not great but did the job. Works fine if you have previously checked that the modelstate is valid, though I can see this solution running into further issues should the modelstate not be valid. – dan richardson Jul 15 '13 at 10:06