Problems Passing ViewModel with data from View to Controller - asp.net-mvc

Ok, here is my problem. I am trying to pass a view model of mine, which has multiple list in it, to my view. Then in my view i need to edit the different list. Then on my post i need to save the edits. Although, when i pass my viewmodel back to my post, it is empty! Can somebody explain what i am doing wrong? i am not worried about saving the information right now, i am just worried about getting the data back to the controller. The ViewModel is null when i hit that portion.
Controller
public ActionResult ManageNewsArticles()
{
NewsViewModel newsViewModel = new NewsViewModel();
newsViewModel.ListBreakingNews = db.NewsArticles.Where(n => n.PageSetupID == 1).ToList<NewsArticle>();
newsViewModel.ListMainArticle = db.NewsArticles.Where(n => n.PageSetupID == 2).ToList<NewsArticle>();
newsViewModel.ListSubNews1 = db.NewsArticles.Where(n => n.PageSetupID == 3).ToList<NewsArticle>();
newsViewModel.ListSubNews2 = db.NewsArticles.Where(n => n.PageSetupID == 4).ToList<NewsArticle>();
newsViewModel.ListSubNews3 = db.NewsArticles.Where(n => n.PageSetupID == 5).ToList<NewsArticle>();
return View(newsViewModel);
}
[HttpPost]
public ActionResult ManageNewsArticles(NewsViewModel newsViewModel)
{
if (ModelState.IsValid)
{
db.SaveChanges();
return RedirectToAction("Admin");
}
return View(newsViewModel);
}
here is my View
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TrueNews.ViewModels.NewsViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Manage News Articles
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Manage News Articles</h2>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm(Model)) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<%: Html.EditorForModel(Model) %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to Admin Controls", "Admin") %>
</div>
</asp:Content>
NewsViewModel
public class NewsViewModel
{
public List<NewsArticle> ListBreakingNews { get; set; }
public List<NewsArticle> ListMainArticle { get; set; }
public List<NewsArticle> ListSubNews1 { get; set; }
public List<NewsArticle> ListSubNews2 { get; set; }
public List<NewsArticle> ListSubNews3 { get; set; }
} // End of Class

I couldn't get your view to display correctly using the EditorForModel syntax, however I did replicate your problem and found this article which provides one way of solving it:
http://weblogs.asp.net/nmarun/archive/2010/03/13/asp-net-mvc-2-model-binding-for-a-collection.aspx
I created a quick table NewsArticle containing Id and Stuff columns and then had the following form in the view:
<% using (Html.BeginForm())
{%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<% for (int i = 0; i < Model.ListBreakingNews.Count; i++)
{ %>
<div>
Id</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Id", i), Model.ListBreakingNews[i].Id) %>
</div>
<div>
Name</div>
<div>
<%= Html.TextBox(string.Format("ListBreakingNews[{0}].Stuff", i), Model.ListBreakingNews[i].Stuff) %>
</div>
<% } %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
and the items in the list were passed when posted. I also found that previously you had Html.BeginForm(Model) which when I added it caused nothing to be passed through in the view model - I am only a newbie to MVC so I don't know why this happens but my code below worked only with Html.BeginForm() and not Html.BeginForm(Model).
I hope this helps.

Related

Post parameter is null

I Have controller with following methods:
public ActionResult Create()
{
return View();
}
[Authorize]
[HttpPost]
public ActionResult Create(Tests test)
{
test.CreateDate = DateTime.Now;
test.Author = User.Identity.Name;
TestEntities db = new TestEntities();
db.AddToTests(test);
db.SaveChanges();
return RedirectToAction("CreateQuestion", new { OrderNumber = 1, idTest = test.id });
}
[Authorize]
public ActionResult CreateQuestion(int OrderNumber,int idTest)
{
return View();
}
[Authorize]
[HttpPost]
public ActionResult CreateQuestion(Questions question)
{
TestEntities db = new TestEntities();
db.AddToQuestions(question);
db.SaveChanges();
return RedirectToAction("CreateQuestion", new {id = question.id, t = question.Type});
}
The problem is Create methods works right. It get parameter and adds it to DB. But similar method CreateQuestion displays message about question is null.
What do I wrong?
CreateQuestion view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<test.su.Models.Questions>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Создать вопрос
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Создать вопрос</h2>
<% using (Html.BeginForm("CreateQuestion","Test")) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Вопрос</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Type,"Тип вопроса") %>
</div>
<% // List of question types
List<SelectListItem> QuestionTypes = new List<SelectListItem>();
SelectListItem t = new SelectListItem();
t.Text = "Вопрос с вариантами ответа (флажки или радиокнопки)";
t.Value = "0";
QuestionTypes.Add(t);
t = new SelectListItem();
t.Text = "Вопрос со свободным ответом (текстовое поле)";
t.Value = "1";
QuestionTypes.Add(t);
%>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Type, QuestionTypes) %>
<%: Html.ValidationMessageFor(model => model.Type) %>
</div>
<%-- <div class="editor-label">
<%: Html.LabelFor(model => model.OrderNumber,"Порядковый номер вопроса") %>
<%: Html.EditorFor(model => model.OrderNumber) %>
<%: Html.ValidationMessageFor(model => model.OrderNumber) %>
</div>--%>
<div class="editor-label">
<%: Html.LabelFor(model => model.Question,"Текст вопроса") %>
</div>
<div class="editor-field">
<%: Html.TextAreaFor(model => model.Question,2,47,"") %>
<%: Html.ValidationMessageFor(model => model.Question) %>
</div>
<%: Html.HiddenFor(model => model.idTest) %>
<%: Html.ValidationMessageFor(model => model.idTest) %>
<%: Html.HiddenFor(model => model.OrderNumber ) %>
<%: Html.ValidationMessageFor( model => model.OrderNumber) %>
<p>
<input type="submit" value="Далее" />
</p>
</fieldset>
<% } %>
</asp:Content>
This is difficult to figure out without knowing the model. Someone else may provide a better answer, but here is the only thing I can think of for now:
If your Questions model looks like this:
public class Questions
{
int Id {get;set;}
string Name {get;set;}
string Description {get;set;}
}
What you can do, for now, is alter your controller to accept the individual parameters and create the object yourself. This might help you figure out which critical property in your Model is missing.
public ActionResult CreateQuestion(string Name, string Description)
{
//make the entity yourself
Questions newQuestion = new Questions()
{
Name = Name,
Description = Description
}
//your other code here
}
Now normally MVC is smart enough to bind your individual values in your form (view) to your model, but some critical value is missing and causing you issue. Once you've figured out what that is, you can actually restore your controller back to accepting only a Questions object.
Sorry I couldn't help you more.
Good Luck.

Object reference not set to an instance of an object in MVC

I looked at all of the postings with this error but none helps. I am using MVC with the views in ASPX (C#).
I am updating records in the database after editing the data in simple views. When I process one record at a time (Controller EDIT methods and EDIT view) all works fine.
When I process multiple records (Controller UpdateTest and UPDATETEST view) I get an error in my HTTPPOST method in Controller after clicking the Submit button in a view.
here is an error with the sample codes one which works and one which does not:
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error: (CONTROLLER – SEE BELOW HttpPost UpdateTest(ICollection<Mysurvey> mysurveys)
enter code here
Line 48: public ActionResult UpdateTest(ICollection<Mysurvey> mysurveys)
Line 49: {
Line 50: foreach (var survey in mysurveys)
Line 51: {
Line 52: db.Entry(survey).State = EntityState.Modified;
Source File: C:\Users\rsc_vok\Documents\Visual Studio 2010 \Projects\MvcMysurvey\MvcMysurvey\Controllers\MysurveyController.cs Line: 50
CONTROLLER
namespace MvcMysurvey.Controllers
{
THE CODE WHICH WORKS – SINGLE EDIT
public ActionResult Edit(int id)
{
Mysurvey mysurvey = db.Mysurveys.Find(id);
return View(mysurvey);
}
// POST: /Mysurvey/Edit/5
[HttpPost]
public ActionResult Edit(Mysurvey mysurvey)
{
System.Diagnostics.Debug.WriteLine("iam in edit post");
if (ModelState.IsValid)
{
db.Entry(mysurvey).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mysurvey);
}
THE CODE WITH THE ERROR IN httppost – MULTIPLE RECORDS SAVE
public ActionResult UpdateTest(int id = 0)
{
List<Mysurvey> mysurveys = db.Mysurveys.ToList();
return View(mysurveys.ToList());
}
[HttpPost]
public ActionResult UpdateTest(ICollection<Mysurvey> mysurveys)
{
foreach (var survey in mysurveys)
{
db.Entry(survey).State = EntityState.Modified;
}
db.SaveChanges();
return View(mysurveys);
}
VIEW
UPDATETEST
THE CODE WHICH WORKS HERE BUT THROWS AN ERROR IN CONTROLLER AFTER THE SAVE BUTTON IS HIT
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MvcMysurvey.Models.Mysurvey>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Updatetest
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<script src="<%: Url.Content("~/Scripts/jquery.validate.min.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js") %>" type="text/javascript"></script>
<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Mysurvey</legend>
<% foreach (var item in Model) { %>
<%: Html.HiddenFor(model => item.ID) %>
<%: Html.HiddenFor(model => item.SurveyID) %>
<div class="editor-label">
<%: Html.LabelFor(model => item.Comment) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => item.Comment) %>
<%: Html.ValidationMessageFor(model => item.Comment) %>
</div>
<%} %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
MODEL
namespace MvcMysurvey.Models
{
public class Mysurvey
{
public int ID { get; set; }
[DisplayFormat(DataFormatString = "{0:c}")]
public int SurveyID { get; set; }
public string Comment { get; set; }
}
public class MysurveyDBContext : DbContext
{
public DbSet<Mysurvey> Mysurveys { get; set; }
}
}

The type or namespace name 'DinnerForm' does not exist in the namespace 'NerdDinner.Models' when trying to implement partial form

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS0234: The type or namespace name 'DinnerForm' does not exist in the namespace 'NerdDinner.Models' (are you missing an assembly reference?)
Source Error:
Line 170:
Line 171: [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()]
Line 172: public class views_dinners_create_aspx : System.Web.Mvc.ViewPage, System.Web.SessionState.IRequiresSessionState, System.Web.IHttpHandler {
Line 173:
Line 174: private static bool #__initialized;
DinnerFormViewModel.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Controllers;
namespace NerdDinner.Models
{
public class DinnerFormViewModel
{
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
}
DinnerForm.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Controllers.DinnerFormViewModel>" %>
<%= Html.ValidationSummary("Please correct the errors and try again.") %>
<% using (Html.BeginForm()) { %>
<fieldset>
<p>
<label for="Title">Dinner Title:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%=Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="EventDate">Event Date:</label>
<%= Html.TextBox("EventDate", Model.Dinner.EventDate) %>
<%= Html.ValidationMessage("EventDate", "*") %>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextArea("Description", Model.Dinner.Description) %>
<%= Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Address">Address:</label>
<%= Html.TextBox("Address", Model.Dinner.Address) %>
<%= Html.ValidationMessage("Address", "*") %>
</p>
<p>
<label for="Country">Country:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%= Html.ValidationMessage("Country", "*") %>
</p>
<p>
<label for="ContactPhone">Contact Phone #:</label>
<%= Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
<%= Html.ValidationMessage("ContactPhone", "*") %>
</p>
<p>
<input type="submit" value="Save"/>
</p>
</fieldset>
<% } %>
DinnersControllers.cs (create methods)
//
// GET: /Dinners/Create
public ActionResult Create()
{
Dinner dinner = new Dinner()
{
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner)
{
if (ModelState.IsValid)
{
try
{
dinner.HostedBy = "SomeUser";
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
catch
{
foreach (var issue in dinner.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(new DinnerFormViewModel(dinner));
}
}
return View(new DinnerFormViewModel(dinner));
}
Create.aspx
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Models.DinnerForm>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Host A Dinner
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Host a Dinner</h2>
<% Html.RenderPartial("DinnerForm"); %>
</asp:Content>
DinnerForm.ascx:
Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.DinnerFormViewModel>

Return a generic list to the MVC Controller

I have a class that looks like this;
public class item
{
public string Tradingname { get; set; }
}
I have a Partial View that inherits from the "item" class;
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %>
In my view I create a number of them. Let's Say 2;
<% using (Html.BeginForm())
{ %>
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<% Html.RenderPartial("TradingName", new Binding.Models.item()); %><br />
<input type="submit" />
<%} %>
Then in my controller I was hoping to be able to write this;
[HttpPost]
public ActionResult Index(List<item> items)
or
[HttpPost]
public ActionResult Index([Bind(Prefix = "Tradingname")]List<item> items)
But I can't seem to get any data back from my partial views into my List. Anyone know how I can get a variable list of data back from a variable set of partialViews?
I would recommend you using editor templates:
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
List<item> model = ...
return View(model);
}
[HttpPost]
public ActionResult Index(List<item> items)
{
...
}
}
and in the view:
<% using (Html.BeginForm()) { %>
<%= Html.EditorForModel();
<input type="submit" />
<% } %>
and finally simply move the partial in ~/Views/Home/EditorTemplates/item.ascx (name and location are important):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<item>" %>
<%= Html.TextBoxFor(x => x.Tradingname) %><br/>

Does ReadOnly(true) work with Html.EditorForModel?

Consider the following setup:
Model:
public class Product
{
[ReadOnly(true)]
public int ProductID
{
get;
set;
}
public string Name
{
get;
set;
}
}
View:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcApplication4.Models.Product>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%= Html.EditorForModel() %>
</asp:Content>
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new Product
{
ProductID = 1,
Name = "Banana"
});
}
}
There result is this:
I was expecting that the ProductID property was not going to be editable via the ReadOnly(true) attribute. Is this supported? If not is there any way to hint ASP.NET MVC that some properties of my model are read-only? I would not like to just hide ProductID via [ScaffoldColumn(false)].
I solved this problem by adding a UIHintAttribute to the property on my class of "ReadOnly".
[UIHint("ReadOnly")]
public int ClassID { get; set; }
Then I simply added a ~\Views\Shared\EditorTemplates\ReadOnly.ascx file to my project with this in it:
<%= Model %>
A really simple way to add custom templates, you could include formatting or whatever.
The ReadOnly and Required attributes will be consumed by the metadata provider but won't be used. If you want to get rid of the input with EditorForModel you'll need a custom template, or [ScaffoldColumn(false)].
For custom template ~/Views/Home/EditorTemplates/Product.ascx:
<%# Control Language="C#" Inherits="ViewUserControl<Product>" %>
<%: Html.LabelFor(x => x.ProductID) %>
<%: Html.TextBoxFor(x => x.ProductID, new { #readonly = "readonly" }) %>
<%: Html.LabelFor(x => x.Name) %>
<%: Html.TextBoxFor(x => x.Name) %>
Also note that the default model binder won't copy a value into a property with [ReadOnly(false)]. This attribute won't influence the UI rendered by the default templates.
<%# Control Language="C#" Inherits="ViewUserControl<Product>" %>
<%: Html.LabelFor(x => x.ProductID) %>
<%: Html.TextBoxFor(x => x.ProductID, new { #readonly = true }) %>
<%: Html.LabelFor(x => x.Name) %>
<%: Html.TextBoxFor(x => x.Name) %>

Resources