Question

I created a view that was working wonderfully until I added some JQuery to support cascading drop downs. I believe in doing that, I broke the binding between the view and the model. I'm getting the error "No parameterless constructor defined for this object." when the form is submitted. The obvious solution would be to add a parameterless constructor, but I'm assuming that the postmodel will be null? Code Snippets below.

Thanks in Advance for your help.

View:

<script type="text/javascript">
$(document).ready(function () {
      $("#ddlCategories").change(function () {
        var iCategoryId = $(this).val();
            $.getJSON(
                 "@Url.Content("~/Remote/SubCategoriesByCateogry")",
                { id: iCategoryId },
                function (data) {
                    var select = ResetAndReturnSubCategoryDDL();
                    $.each(data, function (index, itemData) {
                        select.append($('<option/>', { value: itemData.Value, text: itemData.Text }));
                    });
                });
        });
    function ResetAndReturnSubCategoryDDL() {
        var select = $('#ddlSubCategory');
        select.empty();
        select.append($('<option/>', { value: '', text: "--Select SubCategory--" }));
        return select;
    }
});

...

 <div class="editor-field">
            @Html.DropDownList("iCategoryID", Model.Categories,"--Select Category--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlCategories"}})
            @Html.ValidationMessage("iCategoryID")
        </div>
        <div class="editor-label">
           @Html.LabelFor(model => model.SubCategories, "SubCategory")
        </div>
        <div class="editor-field">
              @Html.DropDownListFor(model => model.SubCategories, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new { id = "ddlSubCategory" })
              @Html.ValidationMessage("iSubCategoryID")
        </div>

Controller:

 [HttpPost]
     public ActionResult Create(VendorCreateModel postModel)
    {
        VendorCreateEditPostValidator createValidator = new VendorCreateEditPostValidator(
            postModel.iCategoryID,
            postModel.iSubCategoryID,
            postModel.AppliedPrograms,
            m_unitOfWork.ProgramRepository,
            new ModelStateValidationWrapper(ModelState));

        if (ModelState.IsValid)
        {
            int categoryId = int.Parse(postModel.iCategoryID);
            int subcategoryId = int.Parse(postModel.iSubCategoryID);
            var programIds = postModel.AppliedPrograms.Select(ap => int.Parse(ap));
            var programs = m_unitOfWork.ProgramRepository.GetPrograms(programIds);

            Vendor vendor = postModel.Vendor;
            vendor.Category = m_unitOfWork.CategoryRepository.GetCategory(categoryId);
            vendor.SubCategory = m_unitOfWork.SubCategoryRepository.GetSubCategory(subcategoryId);

            foreach (Program p in programs)
                vendor.Programs.Add(p);

            m_unitOfWork.VendorRepository.Add(vendor);
            m_unitOfWork.SaveChanges();

            return RedirectToAction("Index");  
        }

        VendorCreateModel model = new VendorCreateModel(
            postModel.Vendor,
            postModel.iCategoryID,
            postModel.iSubCategoryID,
            postModel.AppliedPrograms,
            User.Identity.Name,
            m_unitOfWork.CategoryRepository,
            m_unitOfWork.SubCategoryRepository,
            m_unitOfWork.PermissionRepository);

        return View(model);
    }

RemoteController:

 [AcceptVerbs(HttpVerbs.Get)]
    public JsonResult SubCategoriesByCateogry(int id)
    {
        System.Diagnostics.Debug.WriteLine(id);

        var SubCategories = db.SubCategories
            .Where(v => v.iCategoryID == id)
            .OrderBy(v => v.sDesc)
            .ToList();

        var modelData = SubCategories.Select(v => new SelectListItem()
        {
            Text = v.sDesc,
            Value = v.iSubCategoryID.ToString()
        });

        return Json(modelData, JsonRequestBehavior.AllowGet);
    }

VendorCreateModel:

public class VendorCreateModel
{
    public VendorCreateModel()
    {

    }

    public VendorCreateModel(
        Vendor vendor, 
        string categoryId,
        string subcategoryId,
        IEnumerable<string> appliedPrograms, 
        string username,
        ICategoryRepository categoryRepository,
        ISubCategoryRepository subcategoryRepository,
        IPermissionRepository permissionRepository)
    {
        UserHasProgramsValidator programValidator = new UserHasProgramsValidator(username, permissionRepository);
        var availablePrograms = programValidator.AvailablePrograms;

        HashSet<Category> applicableCategories = new HashSet<Category>();
        foreach (var p in availablePrograms)
            foreach (var c in categoryRepository.GetCategoriesByProgram(p.iProgramID))
                applicableCategories.Add(c);

        this.Vendor = vendor;
        this.AppliedPrograms = appliedPrograms;
        this.Categories = new SelectList(applicableCategories.OrderBy(x => x.sDesc).ToList(), "iCategoryID", "sDesc");
        this.SubCategories = new SelectList(subcategoryRepository.GetAllSubCategories().OrderBy(x => x.sDesc).ToList(), "iSubCategoryID", "sDesc");

        if (!string.IsNullOrEmpty(categoryId))
        {
            int temp;
            if (!int.TryParse(categoryId, out temp))
                throw new ApplicationException("Invalid Category Identifier.");
        }

        this.iCategoryID = categoryId;
        this.iSubCategoryID = subcategoryId;
        this.ProgramItems = availablePrograms
            .Select(p => new SelectListItem()
            {
                Text = p.sDesc,
                Value = p.iProgramID.ToString(),
                Selected = (AppliedPrograms != null ? AppliedPrograms.Contains(p.iProgramID.ToString()) : false)
            });
    }

    public Vendor Vendor { get; set; }
    public SelectList Categories { get; set; }
    public SelectList SubCategories { get; set; }
    public string iCategoryID { get; set; }
    public string iSubCategoryID { get; set; }
    public IEnumerable<SelectListItem> ProgramItems { get; set; }

    [AtLeastOneElementExists(ErrorMessage = "Please select at least one program.")]
    public IEnumerable<string> AppliedPrograms { get; set; }
}
Was it helpful?

Solution

I correct the issue and wanted to share in case someone else was banging their head against their desk like Ihave been. Basically I changed the dropdownlistfor to reflect:

@Html.DropDownListFor(model => model.iSubCategoryID, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlSubCategory"},{"name","iSubCategoryID"}})

OTHER TIPS

Assuming here the problem is in your VendorCreateModel, you either need to add a parameterless constructor or remove it, and create an instance in your action method and populate it by TryUpdateModel. Or parse the form using FormsCollection (not a fan).

You don't have the code for your viewmodel posted here but the basic assumption is that it will map.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top