views:

1171

answers:

2

I'm using an AJAX form to update an item to the database. When it gets done, it returns a partial view that re-lists all the items and displays them all in a table. The problem occurs when I have to add a modelstate error in my controller action. I don't want to return the list of items when there is a modelstate error because I want to show the user the error using the ValidationMessage. My thinking is that I could do something like this in my controller:

       [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult UpdateNewsItem(int newsID, string newsTitle, string newsDescription, string newsBeginningDate, string newsEndingDate)
    {
        List<Models.News> lstNewsItem = new List<News>();

        //we need to grab the member so we can capture the user id 
        //for the corresponding news property
        MembershipUser member = Membership.GetUser(User.Identity.Name);

        //the news instance to use in case the viewdata is invalid
        Models.News newsError = new Models.News();

        //create the datetime objects
        DateTime dtBeginningDate = DateTime.MinValue;
        DateTime dtEndingDate = DateTime.MaxValue;

        //the message we want to send whenever the user enters an invalid date
        string strInvalidDateError = "Invalid date.  Please use a format like '12/25/2008'";

        //clean user input
        newsTitle = Models.clsGlobals.CleanString(newsTitle);
        newsDescription = Models.clsGlobals.CleanParagraph(newsDescription);

        //newsTitle 
        if (string.IsNullOrEmpty(newsTitle))
        {
            newsError.Title = string.Empty;
            ModelState.AddModelError("newsTitle", "You must enter a news title.");
        }

        //description
        if (string.IsNullOrEmpty(newsDescription))
        {
            newsError.Description = string.Empty;
            ModelState.AddModelError("newsDescription", "You must enter a news description.");
        }

        //beginningDate
        if (string.IsNullOrEmpty(newsBeginningDate))
        {
            ModelState.AddModelError("newsBeginningDate", "You must enter a beginning date.");
        }

        //endingDate
        if (string.IsNullOrEmpty(newsEndingDate))
        {
            ModelState.AddModelError("newsEndingDate", "You must enter an ending date.");
        }

        //set the beginning date 
        try
        {
            dtBeginningDate = DateTime.Parse(newsBeginningDate);
            newsError.BeginningDate = dtBeginningDate;
        }

        catch (FormatException)
        {
            ModelState.AddModelError("newsBeginningDate", strInvalidDateError);
        }

        //set the ending date
        try
        {
            dtEndingDate = DateTime.Parse(newsEndingDate);
            newsError.EndingDate = dtEndingDate;
        }

        catch (FormatException)
        {
            ModelState.AddModelError("newsEndingDate", strInvalidDateError);
        }

        //data is validated, so we can begin the update
        if (ModelState.IsValid == true)
        {

            try
            {
                //use to perform actions on db
                Models.NewsDataContext dcNews = new Models.NewsDataContext();

                //fetch the items that match what the user requested to edit
                lstNewsItem = this.GetNewsItem(newsID);

                //set news properties
                foreach (Models.News news in lstNewsItem)
                {

                    news.UserId = (Guid)member.ProviderUserKey;
                    news.Title = newsTitle;
                    news.Description = newsDescription;
                    news.EntryDate = DateTime.Now;
                    news.BeginningDate = dtBeginningDate;
                    news.EndingDate = dtEndingDate;

                }//next

                //update the transaction
                dcNews.SubmitChanges();

                //update the news list
                return PartialView("NewsList", this.GetNewsItems());

            }

            //just to make sure everything goes as planned,
            // catch any unhandled exceptions
            catch (Exception ex)
            {
                ModelState.AddModelError("_FORM", ex);
            }//end catch

        }//end if valid modelstate


        //invalid modelstate, so repopulate the viewdata and
        //send it back

        //the list to hold the entries
        List<Models.News> lstErrorNewsItems = new List<Models.News>();

        //set the remaining error properties
        newsError.UserId = (Guid)member.ProviderUserKey;
        newsError.NewsID = newsID;
        newsError.EntryDate = DateTime.Now;

        //add the item--there will only be one
        //but the view is expecting a list so we will
        //treat it like one
        lstErrorNewsItems.Add(newsError);

        return PartialView("EditNews", lstErrorNewsItems);

    }//end actionresult

The problem is that when a modelstate error occurs, the modelstate viewdata isn't returned. I suspect it's possibly because I'm not specifying an update target id. But I can't set another updatetargetid because I already have one. Any ideas?

+2  A: 
 if (!Model.IsValid)  {
    return PartialView("YourEditForm");
    }
    else 
    {
    return View("YourIndexView");
    }

Should work fine for redisplaying your edit form with validation errors and all. ViewData gets populated from Post data.

Big Update

I've made some testing to figure it out. And came up with a working test project. here are some listings:

My Testing controller

 public class TestController : Controller
    {
                 //
        // GET: /Test/
        List<TestData> data;
        public TestController()
        {
            data = new List<TestData>();
            for (var i = 0; i < 10; i++)
            {
                data.Add(new TestData(){Name=string.Format("TestData{0}",i.ToString().PadLeft(4,'0'))});
            }
        }

        public ActionResult Index()
        {


            return View( data);
        }

        public ActionResult Edit(string name)
        {
            if (Request.IsAjaxRequest())
            {
                return PartialView("AjaxEdit", new TestData() { Name = name });
            }

            return View(new TestData() { Name = name });
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(TestData testData)
        {
            ModelState.AddModelError("name", "incorrect name");
            if (!ModelState.IsValid)
            {
                if (Request.IsAjaxRequest())
                {
                    return PartialView("AjaxEdit");
                }


            }

            return View();
        }
    }

My Edit View:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AjaxValidationPartial.Models.TestData>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit</h2>
    <div id="editForm">
<% Html.RenderPartial("AjaxEdit",Model );%>
</div>
</asp:Content>

My Ajax Partial Edit View

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>

<% using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "editForm" }))
   {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="Name">Name:</label>
            <%= Html.TextBox("Name")%>
            <%= Html.ValidationMessage("Name", "*")%>
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

<% } %>

<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>

And my TestData model class

  public class TestData
    {
        public string Name { get; set; }
    }

with this code it works just as you wanted. notice that i do not pass any model to partial view in my "Post" Edit action. View that gets rendered gets all the values it needs from a post request.

Alexander Taran
Thanks. I've tried this, but the problem is that I think "return PartialView" is returning a new instance, which isn't getting displayed on the page. How can I display it?
Austin
Thanks! This works, but I still have a problem--changing the updatetargetid breaks my old updatetargetid. When the form submission is valid, I want to redisplay the news items using the updatetargetid of "newsTable." There's no way to have multiple Target ID's.
Austin
A: 

Alright, I've figured it out. Thanks for the response. For future reference, what I had to do was set up one master divider that all of my content goes into. Then, I always set the updateTargetID to that divider so that it doesn't matter what content it displays, just that it displays it. This actually turns out to be easier because you don't have to waste Javascript functions setting other div tags on and off because you're only using one that is continuously updated.

Austin