Very simply I have data in a table on a server that does not appear when my ASP.NET MVC page is compiled into HTML.
I know everything on the backend works, because on other views there is no issue, but for some reason on this one page it doesn't work.
I suspect it has something to do with my model having two classes in it, because this is the only page to do that, but I'm not really sure as I am a novice at MVC.
Here's the Model, which I'll refer to as ModelFile.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Project.Subgroup.Models
{
public class ClassIActuallyNeed
{
public int Id { get; set; }
public string Html { get; set; } //<---This is the data I want
public string Version { get; set; }
public bool Deleted { get; set; }
}
public class ClassMyFriendInsistsINeed
{
public int Id { get; set; }
public string Name { get; set; }
public byte[] Signature { get; set; }
public bool Deleted { get; set; }
public virtual ClassIActuallyNeed ClassIActuallyNeed { get; set; }
}
}
...and the Controller which is called MainController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Project.Models;
using Project.Subgroup.Models;
using System.Data.Entity;
using Project.Models.Authentication;
namespace Project.Subgroup.Controllers
{
public class MainController : Controller
{
private MyDatabase context = new MyDatabase();
// GET: Lobby/Home
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult FormPage()
{
return View();
}
[HttpPost]
public ActionResult FormPage(ClassIActuallyNeed model)
{
if (ModelState.IsValid)
{
using (var context = new MyDatabase())
{
var modelfile = new ModelFile
{
Name = model.Name,
Signature = model.Signature,
ClassIActuallyNeedId = model.ClassIActuallyNeedId
};
}
}
return View(model);
}
}
}
...and finally the view, which is FormPage.cshtml:
#model Project.Models.ClassIActuallyNeed
#{
ViewBag.Title = "My Page";
Layout = "~/Subgroup/Views/Shared/_Layout.cshtml";
}
<h2 >#ViewBag.Title</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.DisplayFor(model => model.Html) //<---- the problem
//There is other form stuff in here
}
The "Html" data is literally just a string containing HTML that I want plopped into my webpage. It's the only line in that table, and when I use "DisplayNameFor" instead of "DisplayFor", it successfully puts the name "Html" on my page.
Sorry for the code vomit, I cut out everything I knew was unrelated but I just can't really pin down exactly where my problem is. There are no errors of any kind in visual studio or in the console. Everything else on the page works totally fine including my JS stuff.
Edit: I appreciate all the input so far. I know that the problem is not that I don't have the model populated, as it is populated by the remote database. This works on every other page.
As I suspected the RequestVerificationToken is definitely supposed to be there, and is unrelated.
Also to clarify: the issue is not that it is displaying unformatted text instead of HTML. The issue is that it is displaying nothing at all.
You have 2 issues currently that I can see:
You're not populating your model with any data
[HttpGet]
public ActionResult FormPage()
{
//you need to pass the view some data, otherwise your model in your view
//will be null and you wont get any output (which is what you're seeing now)
ClassIActuallyNeed model = null;
//hardcoded:
//model = new ClassIActuallyNeed
//{
// Id = 1,
// Html = "<h1>Hello</h1>",
// Version = "something,
// Deleted = false
//};
//an example of using your context to fetch your model
using(var context = new MyDatabase())
{
var id = 1
model = context.ClassIActuallyNeeds.Single(x => x.Id == id);
}
return View(model);
}
You're not rendering the html string as html
Html.DisplayFor(model => model.Html) will render the this property as plain text on the page so instead of seeing Hello formatted in a <h1> tag, you'll see <h1>Hello</h1> as plain text on the page. The framework does this to protect against injection attacks which I'd suggest you read up on a bit however that's out of scope as far as this post goes. To have the html string actually rendered, use Html.Raw(model => model.Html)
As an additional side note, the
<input name="__RequestVerificationToken" type="hidden" value="[a bunch of junk characters here]">
you're seeing is a result of you using a #Html.AntiForgeryToken(). The antiforgery token is used to protect against CSRF attacks which I'd also suggest you read up on (security is important!). Since this view does not have any editable data in it (that's an assumption I'm making based on you using DisplayFor), there's no reason I see to have a form (Html.BeginForm) in your view or a AntiForgeryToken. If this data is editable and can be posted back to the server, then disregard that and both of those 2 pieces likely need to stay in place.
With the controller action above, your view could simply look like the following to display your data:
#model Project.Models.ClassIActuallyNeed
#{
ViewBag.Title = "My Page";
Layout = "~/Subgroup/Views/Shared/_Layout.cshtml";
}
<h2 >#ViewBag.Title</h2>
#Html.Raw(model => model.Html)
If this data needs to be editable then it would look more like
#model Project.Models.ClassIActuallyNeed
#{
ViewBag.Title = "My Page";
Layout = "~/Subgroup/Views/Shared/_Layout.cshtml";
}
<h2 >#ViewBag.Title</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.Version)
#Html.HiddenFor(model => model.Deleted)
#Html.EditorFor(model => model.Html)
<input type="submit" value="Submit">
}
Related
UPDATED POST
I am currently developing a Survey application that uses MVC4, Razor, EF5. There will be various surveys an will be utilizing one view. The Models for the application comes from an existing database. Apart from that, I created a separate Entities for the following:
SurveyDisplay - Model for elements that will appear on the survey page because it consists of different languages.
SurveyInfo - Model for information where data comes from a web service.
SurveyQuestion - Model for the questionnaires of the survey.
SurveyChoices - Model for the choices of each question in the survey.
SurveyAnswers - Model to retrieve choices of answers
UPDATE IMAGE(Added SurveyAnswers)
Kindly refer to the image below for the following fields:
I am able to get the particular values to display in my page which is a Razor, but upon POST. I am getting ModelState.IsValid == false. All models are null except for SurveyAnswers.
Here's how my code goes so far:
SurveyRepository:
For this part, I imported stored procedures to get data from the database.
public List<SurveyQuestion> GetQuestions(int surveyid)
{
using (var ctx = new ICSDBContext())
{
return ctx.GetSurveyQuestions(surveyid).ToList<SurveyQuestion>();
}
}
public List<SurveyChoice> GetChoices(int surveyid)
{
using (var ctx = new ICSDBContext())
{
return ctx.GetSurveyChoices(surveyid).ToList<SurveyChoice>();
}
}
public List<SurveyDisplay> GetSurveyDisplay(int surveyid)
{
using (var ctx = new ICSDBContext())
{
return ctx.GetSurveyDisplay(surveyid).ToList<SurveyDisplay>();
}
}
SurveyController:
using ICS.Repositories;
using ICS.ViewModels;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
public class SurveyController : Controller
{
SurveyRepository surveyRepository = new SurveyRepository();
SurveyViewModel surveyViewModel = new SurveyViewModel();
[HttpGet]
public ActionResult Index(int surveyid, string rowid)
{
var surveyDisplay = surveyRepository.GetSurveyDisplay(surveyid);
var surveyQuestions = surveyRepository.GetQuestions(surveyid);
var surveyChoices = surveyRepository.GetChoices(surveyid);
string headerText = "";
string messageBody = "";
string buttonText = "";
foreach (var item in surveyDisplay)
{
headerText = item.HeaderText;
messageBody = item.MessageBody;
buttonText = item.ButtonText;
}
surveyViewModel.HeaderText = Server.HtmlEncode(headerText);
surveyViewModel.MessageBody = Server.HtmlEncode(messageBody);
surveyViewModel.Buttontext = Server.HtmlEncode(buttonText);
surveyViewModel.SurveyQuestions = surveyQuestions;
surveyViewModel.SurveyChoices = surveyChoices;
return View("Survey", surveyViewModel);
}
//Not the actual code yet. I'm trying to check in this action whether there is POSTed data or none.
[HttpPost]
}
SurveyViewModel
public class SurveyViewModel
{
public List<SurveyInfo> SurveyInfo { get; set; }
public string HeaderText { get; set; }
public string MessageBody { get; set; }
public string Buttontext { get; set; }
public List<SurveyQuestion> SurveyQuestions { get; set; }
public List<SurveyChoice> SurveyChoices { get; set; }
public List<SurveyAnser> SurveyAnsers { get; set; }
}
Razor View
<h2>#Html.DisplayFor(model => model.HeaderText)</h2>
<div id="info">
<p>#Html.DisplayFor(model => model.MessageBody)</p>
<div class="margTop20">
<ul>
#for (var info = 0; info < Model.SurveyInfo.Count(); info++)
{
<li>
<span>Case ID: </span>
<b>#Html.DisplayFor(model => model.SurveyInfo[info].SRNumber)</b>
</li>
<li>
<span>Description: </span>
<b>Others</b>
</li>
<li>
<span>Problem Category: </span>
<b>#Html.DisplayFor(model => model.SurveyInfo[info].ProblemSubCategory)</b>
</li>
<li>
<span>Product: </span>
<b>#Html.DisplayFor(model => model.SurveyInfo[info].Product)</b>
</li>
<li>
<span>Method of Service: </span>
<b>#Html.DisplayFor(model => model.SurveyInfo[info].SupportType)</b>
</li>
}
</ul>
</div>
</div>
#for (var question = 0; question < Model.SurveyQuestions.Count(); question++)
{
<div id="#("question" + ConvertNumberToWords.Translate(question))" class="#(Convert.ToBoolean(Model.SurveyQuestions[question].Subquestion) == true ? "subquestion" : "questions")">
<p>
<b>
#Html.DisplayFor(model => model.SurveyQuestions[question].TheQuestion)
</b>
</p>
#Html.HiddenFor(model => model.SurveyAnswers[question].QuestionID)
#if (Convert.ToBoolean(Model.SurveyQuestions[question].Mandatory) == true)
{
<p><span id="#("errorQuestion" + ConvertNumberToWords.Translate(question))" class="errorMsg">*Please choose your answer</span></p>
}
#for (var choice = 0; choice < Model.SurveyChoices.Count(); choice++)
{
if (Model.SurveyQuestions[question].QuestionID == Model.SurveyChoices[choice].QuestionID)
{
if (Model.SurveyChoices[choice].isStyleOptBox)
{
var choicesGroup = (from c in Model.SurveyChoices where c.QuestionID == Model.SurveyQuestions[question].QuestionID select new { c.ChoicesID, c.ChoicesName });
#Html.Raw("<ul>")
#Html.Raw("<li>")
#Html.RadioButtonForSelectList(model => model.SurveyAnswers[question].ChoiceID, new SelectList(choicesGroup, "ChoicesID", "ChoicesName"))
#Html.Raw("</li>")
#Html.Raw("</ul>")
break;
}
else if (Model.SurveyChoices[choice].isStyleChkBox)
{
var choicesGroup = (from c in Model.SurveyChoices where c.QuestionID == Model.SurveyQuestions[question].QuestionID select new { c.ChoicesID, c.ChoicesName });
#Html.Raw("<ul>")
#Html.Raw("<li>")
#Html.CheckBoxListFor(model => model.SurveyAnswers[question].ChoiceID, model => choicesGroup, model => model.ChoicesID, model => model.ChoicesName, model => false, Position.Vertical)
#Html.Raw("</li>")
#Html.Raw("</ul>")
break;
}
else if (Model.SurveyChoices[choice].isStyleCboBox)
{
}
else
{
<div class="margTop20">
<p>
#*<textarea cols="" rows="5" class="form-control"></textarea>*#
#Html.TextAreaFor(model => model.SurveyAnswers[question].Comment, new { rows = "5", cols = "0", #class = "form-control" })
</p>
</div>
}
}
}
</div>
}
</div>
<input id="hidQuestionCount" type="hidden" value="#Model.SurveyQuestions.Count()" />
<div>
#*<a class="btn btn-primary" href="#myModal" id="btnSubmit">#Model.Buttontext</a> #Url.Action("Submit", "SaveSurvey", Model)*#
<input id="btnSubmit" class="btn btn-primary" type="submit" value="#Model.Buttontext" />
</div>
If you will notice, I am using a custom Html Helper to render radio button groups which is RadioButtonSelectListFor which is very handy in this scenario. I am able to bind and get the value of the selected control as you can see in the image below:
Secondly, I am also using the Html Helper package Hmtl Helper CheckBoxListFor to display group of checkboxes to make multiple selections and submit it. But the problem is, I am getting 1 value among all checkboxes which causes me real pain and headache. When there is 2 or more checkboxes checked, only the first item is being returned, you may refer to the image below:
For the comments value, I have no problem with it as I can get the values. As seen on the image:
I also have one problem, I need to bind QuestionID in SurveyAnswers because it is used for reporting purpose.
To sum up, below are my things to achieve:
OBJECTIVES:
Bind QuestionID to model SurveyAnswers
Return all checkboxes value and add it to list SurveyAnswers
If possible, make ModelState.IsValid to be true. If not, I won't be validating model to get the list of SurveyAnwsers
I really want to get this thing to work. I have been doing a lot of research just to get it going, but no progress yet. Kindly help me guys! Any inputs/ suggestions will be highly appreciated.
Thank you very much!
The issue is with these lines in your view model:
public List<SurveyInfo> SurveyInfo { get; set; }
...
public List<SurveyQuestion> SurveyQuestions { get; set; }
public List<SurveyChoice> SurveyChoices { get; set; }
Because you're referencing full entities, presumably each with their own required properties, you need to ensure that each of those required properties is posted back with some value or it invalidates your entire model, because that individual entity is invalid. However, you're not actually creating any of these entities, but rather just displaying existing instances. As a result, you should be using a view model for each of these as well. The only thing that should be required at all on your view model or anything referenced by your view model is the actual data you want to collect from the user.
UPDATE
This is all about how the model binder works and how it determines if your model is valid or not. It looks at it as if you would want to save this entire thing to a database, as if you had a table that matched up with your view model (even though you don't actually). So, in order for the view model to be "valid", it would have to be able to save everything else on the view model as well (your SurveyInfo, SurveyQuestions and SurveyChoices properties. If there's any required properties on any of those classes, that isn't posted back (which there of course are), then they are invalid, and your view model is invalid as a result. Now, that said, there's conditions. Simply because you attached a list of SurveyQuestions doesn't mean that you have to have a list of valid SurveyQuestion instances in your POST body to allow the view model to validate. If a list or relationship is null, then it is not validated (assuming that the list or instance, itself, is not required). So, really where you're going wrong here is in posting back partial instances of these things. Because it's ending up with a list of incomplete SurveyQuestion instance, it's invalid, whereas if you simply posted nothing back at all, it would be valid.
In other words, you can keep the lists as they are, to pull information from as you need it in your view, but, you need to attach POST data somewhere else. Create another list property like:
public List<SurveyAnswerViewModel> SurveyAnswers { get; set; }
And that would be the only thing that you posted to. Then, as long the user fills out the survey correctly as defined in SurveyAnswerViewModel, you'd have a valid view model. Just be aware that since you're not posting the other lists back, you have to repopulated them should the view need to be returned back to correct an error.
I am unable to make a simple MVC 3 controller/view/model program work with an ActionResult method that includes the Bind attribute with a Prefix property.
One example that I did try could populated the parameter when I call the action method from the URL.
Here is that example controller followed by its view:
//Controller
//public ActionResult PrefixExample(int number = 0)
public ActionResult PrefixExample([Bind(Prefix="okay")]int? number)
{
return View(number);
}
//View
#model Int32?
#{
ViewBag.Title = "Example";
}
<h2>Example</h2>
#using (Html.BeginForm())
{
if (#Model.HasValue)
{
<label>#Model.Value.ToString()</label>
} else {
<label>#Model.HasValue.ToString()</label>
}
<input type="submit" value="submit" />
}
If I use this url http://localhost/MVCApp/Home/Example?okay=3 the parameter, number, is populated. If I use this url http://localhost/MVCApp/Home/Example?number=3, the parameter isn't populated. Interestingly, with the first url, when I view source, the prefix okay doesn't show up.
If I uncomment the first line of my controller and comment out the second line, the opposite is true: the url with okay won't populate number but the second url using number will populate number in the controller.
I would like to know how to make the following example accept a url and correctly set the "view source" prefix. Here is a possible url http://localhost/MVCApp/Home/SpecificPerson?PersonId=0&FirstName=Joe&LastName=Doe
Note, that if I remove the Bind attribute from the controller method, the above url will work with the MVC app below.
Here is my model/controller/view:
//model:
namespace MVCApp.Models
{
public class Person
{
[HiddenInput(DisplayValue = false)]
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
//controller
namespace MVCApp.Controllers
{
public class HomeController : Controller
{
public ActionResult SpecificPerson([Bind(Prefix = "myPerson")]Person aPerson)
{
return View("SpecificPerson", aPerson);
}
}
}
//view
#model MVCApp.Models.Person
#{
ViewBag.Title = "SpecificPerson";
}
<h2>SpecificPerson</h2>
#Html.EditorForModel();
<br />
#Html.EditorFor(m => m);
I would like to see the above example work. Anyone who could show me why it doesn't work as I expect or what I can do to make it work this way would be greatly appreciated.
Thank you in advance.
I think the EditorForModel brought you a bit off track. If you check the html that is generated by this helper you will see that it's not wrapped in a form. Besides that I think the EditorForModel will not serve you as much as you would like to. You can also get it to work correctly without specifying the Bind prefix.
//model
public class Person
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
//controller
public class HomeController : Controller
{
public ActionResult Index(Person person)
{
if("POST".Equals(Request.HttpMethod))
{
//for example do some validation and redirect
}
return View(person);
}
}
//view
#model Application.Models.Person //only as example use own
#using(Html.BeginForm("Index","Home", FormMethod.POST))
{
#Html.HiddenFor(x=> x.Id)
<div>
#Html.LabelFor(x=> x.FirstName)
#Html.TextBoxFor(x=> x.FirstName)
</div>
<div>
#Html.LabelFor(x=> x.LastName)
#Html.TextBoxFor(x=> x.LastName)
</div>
<input type="submit" value="Do a post request"/>
}
Also if you use a url like /Home/Index?Id=9 and you look the HTML code you will see that there will be a element with input type=hidden and the value of 9. You could also use two actionresults to split your logic with [HttpGet] and [HttpPost] as attribute of your Action.
And as last I recommend you to check out the newer versions of MVC; MVC 5 is already out...
I have a partial view to post comment for my article module on my main view for article detail. Model for comment has three required fields, ID (identity field), ArticleId and CommentText. (I am using Razor syntax)
I tried to pass ArticleId at controller in Create Action.
public ActionResult Create(ArticleComment articlecomment, string AID)
{
articlecomment.ArticleId = AID; //this is required
if (User.Identity.IsAuthenticated)
{
articlecomment.UserId = WebSecurity.CurrentUserId.ToString();
}
else
{
articlecomment.UserId = Constants.Anonymus;
}
articlecomment.CommentDate = DateTime.Now;
if (ModelState.IsValid)
{
db.ArticleComment.Add(articlecomment);
int success = db.SaveChanges();
if (success > 0)
{
return Content("<script language='javascript' type='text/javascript'>alert('Comment added successfully.');window.location.href='" + articlecomment.ArticleId + "';</script>");
}
else
{
return Content("<script language='javascript' type='text/javascript'>alert('Posting comment has failed, please try later.');window.location.href='" + articlecomment.ArticleId+ "';</script>");
}
}
return PartialView(articlecomment);
}
But still ModelState.IsValid is returning false. I have used following code and find that ModelState is getting ArticleId as null.
foreach (var modelStateValue in ViewData.ModelState.Values)
{
foreach (var error in modelStateValue.Errors)
{
// Do something useful with these properties
var errorMessage = error.ErrorMessage;
var exception = error.Exception;
}
}
I have also thought to set value for ArticleId using Hidden field using ViewBag but have not find any working code. I tried following:
#Html.HiddenFor(model => model.ArticleId, new { #value = ViewBag.Article })
and
#Html.HiddenFor(model => model.ArticleId, (object)ViewBag.Article)
My 'ParticalView' to post comment is:
#model Outliner.Models.ArticleComment
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#* #Html.HiddenFor(model => model.ArticleId, new { #value = ViewBag.Article })
#Html.HiddenFor(model => model.ArticleId, (object)ViewBag.Article)*#
#Html.LabelFor(model => model.Comment)
<span class="error">#Html.ValidationMessageFor(model => model.Comment)</span>
</div>
#Html.TextAreaFor(model => model.Comment)
<input type="submit" value="Post" />
}
And this is how I am calling this partial view on 'ArticalDetail' view (my main view):
#Html.Action("Create", "ArticleComment")
I have passed required field value at controller for a View before, but I am facing issue for PartialView. What I am doing wrong and how can I make this work?
Edit After a try
As Satpal and Fals lead me to a direction, I tried their suggestions, and tried following:
TryUpdateModel(articlecomment);
and also
TryUpdateModel<ArticleComment>(articlecomment);
and also
TryValidateModel(articlecomment);
but I was still getting same validation error for ArticleId, then I checked in Watch and all tree methods I tried are returning False.
I also tried following:
UpdateModel(articlecomment);
and
UpdateModel<ArticleComment>(articlecomment);
above methods are generating an exception :
The model of type 'Outliner.Models.ArticleComment' could not be
updated.
Here is my model:
[Table("ArticleComments")]
public class ArticleComment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string ArticleId { get; set; }
public string UserId { get; set; }
[Required]
[Display(Name = "Comment")]
public string Comment { get; set; }
[Required]
[Display(Name = "Commented On")]
public DateTime CommentDate { get; set; }
}
I don't get it, why my model is not updating ... :(
It is quite late answer, but it should work.
Before ModelState.IsValid, add following
ModelState.Remove("ArticleId");
It will remove that field from validation.
You can try TryUpdateModel(articlecomment) once before checking ModelState.IsValid. However I have not tested it
After update any requerid field after the ModelBind you must call another method to update the validation.
You can use:
TryValidateModel(articlecomment);
or
TryUpdateModel<ArticleComment>(articlecomment);
To me it seems that your #Html.Action(...) code it invoking the action to create the partial view, like you said. If you are doing this it isn't the correct way to invoke a partial view. While it isn't uncommon for a action to return a partial view, it is normally via AJAX, in my experience, so you can just insert it into the DOM after it returns.
You can use the following method to render a partial view:
#{
Html.RenderPartial("_myPartialView",
new ArticleComment {ArticleId = model.Id});
}
This should render your partial view, pass your model to it so it can render properly. Then when the form is POST'ed to the server it should create the model from the form data. You shouldn't need the AID parameter as it is part of your ArticleComment model.
I'm new to MVC and new to programming (in a general sense) as well.
I'm struggling with the concept of how to post multiple database entries into my database (via my controller) based on values I'm pulling from a MultiSelectList that I've populated.
Basically, I'm trying to create a simple model that contains a State and a City, and lets the user select and save multiple City values to their account based on State/City values pulled from a separate, static database.
Here's my Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace BidFetcher.Models
{
public class SPServiceLocation
{
public int SPServiceLocationID { get; set; }
public int SPCompanyAccountID { get; set; }
[Display(Name = "State")]
public string state { get; set; }
[Required]
[Display(Name = "Cities (select all that apply)")]
public string city { get; set; }
public virtual SPCompanyAccount SPCompanyAccount { get; set; }
}
}
Here is a snippet of my View data (including the multi-select list for city that I have no trouble populating):
<div class="editor-label">
#Html.LabelFor(model => model.state)
</div>
<div class="editor-field">
#Html.DropDownList("state", ViewBag.state as SelectList, "Select a state...", new { #class = "chzn-select", onchange = "CityChange()" })
#Html.ValidationMessageFor(model => model.state)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.city)
</div>
<div class="editor-field" id="SP_SelectCity">
#Html.ListBox("city", ViewBag.city as MultiSelectList, new { #class = "chzn-select", data_placeholder = "Select cities..." })
#Html.ValidationMessageFor(model => model.city)
</div>
<p>
<input type="submit" value="Submit" />
</p>
And here are my Create/Post Controllers:
public ActionResult Create()
{
ViewBag.sessionName = HttpContext.Session["SPCompanyName"].ToString();
var compID = HttpContext.Session["SPCompanyAccountID"].ToString();
ViewBag.companyID = compID;
ViewBag.state = new SelectList(simpleDB.simpleState, "simpleStateID", "simpleStateID");
ViewBag.city = new MultiSelectList(simpleDB.simpleCity, "cityFull", "cityFull");
return View();
}
[HttpPost]
public ActionResult Create(SPServiceLocation spservicelocation, FormCollection formValues)
{
if (ModelState.IsValid)
{
db.SPServiceLocation.Add(spservicelocation);
db.SaveChanges();
return RedirectToAction("Create", "SPServiceLocation");
}
return View(spservicelocation);
}
As you can see, I'm missing something in my [HttpPost] that'll allow me to save multiple values to the db database, but I'm not sure what exactly it is that's missing. I've seen some posts that explain this in terms of creating an IEnumerable list, but I guess I'm just not sure I need to do that since I'm already successfully populating my MultiSelectList via a database call.
Any help is greatly appreciated!
EDIT:
Based on the answers below, if I want to create multiple new database rows based on the results I collect from a MultiSelectList, I need to use Form collection to grab those results and parse them within my HttpPost:
And... I do not know how to do that. I assume something along these lines:
[HttpPost]
public ActionResult Create(SPServiceLocation spservicelocation, FormCollection formValues)
{
if (ModelState.IsValid)
{
foreach (var item in x)
{
db.SPServiceLocation.Add(spservicelocation);
db.SaveChanges();
}
return RedirectToAction("Create", "SPServiceLocation");
}
return View(spservicelocation);
}
Something along the lines of the above, where I create a collection based on my multiselectlist, break it into multiple variables, and step through my database.SaveChanges multiple times before I redirect?
Thanks!
The trick has nothing to do with your MVC code. You have to change either your Business Layer or Data Access Layer (DAL) code to read/write to 2 different databases.
I did the same project to answer one question sometime ago. Here it is: https://stackoverflow.com/a/7667172/538387
I checked the code, It's still working, you can download it and check it yourself.
When user submits the Create forms and application flows to [HttpPost] public ActionResult Create action, all of the form data live in formValues parameter.
You have to read those data from formValues (like: formValues["companyID"], build appropriate model object from them, and then update the db.
I have a problem in mvc3. Im not sure this is specifically mvc3 but, Im currently using that with razor engine. Anyway, the problem that I am facing is, I have a form, I use the TextBoxFor, CheckBoxFor, etc. to render it. The rendering is working flawless, except, when I try to post the data, I basically post an empty form with null values.
Here is my model:
public class SendReplyPmForm : PM
{
public new string Text { get; set; }
public bool IsOriginalDelete { get; set; }
public int ReplyNr { get; set; }
public string ReceiverName { get; set; }
}
I have an extra viewmodel layer between the view and the model and it contains an extra paramater regarding this model
public class IndexViewModel
{
public SendReplyPmForm SendReplyPmForm { get; set; }
...
here is my view
#using (Html.BeginForm("SendReply", "Pm", FormMethod.Post, new { id = "formSendMsg" }))
{
#Html.TextBoxFor(model => model.SendReplyPmForm.ReceiverName, new { id = "ReceiverName" })
<span id="spanChkText">Delete Original Message: #Html.CheckBoxFor(model => model.SendReplyPmForm.IsOriginalDelete, new { id = "chkIsOriginalDelete", value = 1 })</span>
#{Html.RenderPartial("~/Areas/Forums/Views/Shared/Toolbar.cshtml");}
<span class="spanLabel">Message</span>
#Html.TextAreaFor(model => model.SendReplyPmForm.Text, new { id = "Text", rows = "10", cols = "65" })
#{Html.RenderPartial("temp.cshtml");}
#Html.HiddenFor(model => model.SendReplyPmForm.ReplyNr, new { id = "inputReplyNr", value = 0 })
<input type="submit" value="Send" />
}
and here i have the controller
[HttpPost]
public ActionResult SendReply(SendReplyPmForm SendReplyForm) {
var ViewModel = new IndexViewModel();
.
.
.
return View("Index", ViewModel);
}
The strange thing is, if I use pure HTML instead of the Html helpers, then the post is going smoothly without any problem.
I read this article (ASP.NET MVC’s Html Helpers Render the Wrong Value!) before I post this, but im not quite sure, that I have the same issue. (f.e.: I dont have action in my controller, that has the same name), but the fact that is also working with pure Html that makes me think.
Do you guys have any idea, how can I use this form with the Html helpers?
I believe your problem is the same as here, so you need to add a prefix:
[HttpPost]
public ActionResult SendReply([Bind(Prefix="SendReplyPmForm")]SendReplyPmForm SendReplyForm)
{
...
}