mcintyre321 / FormFactory

MVC5, Core or standalone - Generate rich HTML5 forms from your ViewModels, or build them programatically
http://formfactoryaspmvc.azurewebsites.net/
MIT License
304 stars 102 forks source link

NullReferenceException while returning invalid vievmodel #32

Closed mikolajtr closed 7 years ago

mikolajtr commented 7 years ago

When viewmodel is passed again to form as invalid (ModelState.IsValid == false), form in my Razor view throws System.NullReferenceException.

My viewmodel

public class CategoryViewModel
    {
        public CategoryViewModel()
        {
            CategoryProperties = new List<CategoryPropertyViewModel>();
        }

        [Hidden]
        public int Id { get; set; }

        [Required]
        [MaxLength(100)]
        [DisplayName("Category name")]
        public string Name { get; set; }

        [DisplayName("Base category")]
        public int BaseCategoryId { get; set; }

        public IEnumerable<int> BaseCategoryId_choices()
        {
            var categoryService = CategoryService.GetService();
            var data = categoryService.GetCategories();

            return data.Select(x => x.Id.DisplayName(x.Name));
        }

        public ICollection<CategoryPropertyViewModel> CategoryProperties { get; set; } 
    }

public class CategoryPropertyViewModel
    {
        [Hidden]
        public int Id { get; set; }

        [Required]
        [MaxLength(100)]
        [DisplayName("Property name")]
        public string Name { get; set; }

        [Required]
        [DisplayName("Property type")]
        public PropertyType PropertyType { get; set; }

        [Hidden]
        public int CategoryId { get; set; }
    }

My Razor view:

@using FormFactory
@using FormFactory.AspMvc
@model Reservations.DataUI.Models.CategoryViewModel

<h2>@ViewData["Title"]</h2>

@using(Html.BeginForm(ViewData["Action"].ToString(), "Categories", FormMethod.Post, new {@class = "form-horizontal", role = "form"}))
{
    @FF.PropertiesFor(Model).Render(Html)

    <button type="submit" class="btn btn-info">Save</button>
}

My POST controller method:

[HttpPost]
public ActionResult Add(CategoryViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View("Update", model);
    }
    return RedirectToAction("Index");
}

My GET controller method:

public ActionResult Add()
{
    ViewData["Title"] = "Add category";
    ViewData["Action"] = "Add";
    var model = new CategoryViewModel();
    return View("Update", model);
}

Stack trace:

ASP._Page_Views_Categories_Update_cshtml.Execute() in C:\Users\trawinski\Documents\reservations_clean\Reservations.DataUI\Views\Categories\Update.cshtml:7
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +270
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +122
   System.Web.WebPages.StartPage.RunPage() +63
   System.Web.WebPages.StartPage.ExecutePageHierarchy() +100
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +131
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +695
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +382
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +431
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +116
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +529
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +106
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +321
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +185
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +37
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +39
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +37
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +39
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +37
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9765121
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
mcintyre321 commented 7 years ago

So I suspect the POSTed model is null, which is then being reflected over, causing the NRE.

Can you use Fiddler/Chrome Dev tools to see what is being POSTed?

mikolajtr commented 7 years ago

The model object is not null, only empty fields are. Besides normal form fields, I see in Fiddler __type field with FormFactory.NonEncodingStringencoder value(but it's normal, I understand).

mcintyre321 commented 7 years ago

do you need to set the ViewData values again? It looks like the NRE is being thrown in the view, not FormFactory code, and I don't see ViewData["Title"] and ViewData["Action"] being set.

mikolajtr commented 7 years ago

Yes, now I see it. It was null from ViewData. It was strange for me that I didn't have this error when I use unassigned value from it.