Currently I have a view which takes an IEnumerable model which I use to display data on the view. However on the same view I also have a modal popup in which I want to add to the model rather than separating them out into different views. I tried to follow the suggestion at the bottom of this question How to access model property in Razor view of IEnumerable Type? but got an exception
The expression compiler was unable to evaluate the indexer expression '(model.Count - 1)' because it references the model parameter 'model' which is unavailable.
At the top of the view I have
#model IList<Test.Models.DashModel>
and within my modal body I have
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DashboardModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model[model.Count - 1].DashName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[model.Count - 1].DashName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model[model.Count - 1].DashName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model[model.Count - 1].CreatedDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model[model.Count - 1].CreatedDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model[model.Count - 1].CreatedDate, "", new { #class = "text-danger" })
</div>
</div>
</div>
}
I agree that overuse of the word Model, i.e. the #model keyword, the Model instance variable of the same type, and the default name model given to the lambda parameter name of the HtmlHelper methods is really confusing.
Unfortunately model in this case is the parameter passed by the Html.*For extension methods into your lambda. IMO the scaffolded views could have chosen a less conflicting parameter variable name for the lambdas, e.g. m or x etc.
To access the actual ViewModel instance passed to the view (i.e. #model defined at the top of your razor .cshtml, viz #model IList<Test.Models.DashModel>), what you want to do is to access Model (note the case difference):
#Html.LabelFor(model => Model.Last().CreatedDate, ...
I would also recommend using the Linq extension methods such as Last() / First() etc rather than using the array indexers.
Out of interest, you can of course change the parameter name to anything you like, e.g.
#Html.LabelFor(_ => Model.Last().CreatedDate, ...
Related
I have the following code, but it return me an empty FormationDTO object, did I do anything wrong?
I don't understand why it can't properly bind FormationFormViewModel's FormationDTO to the action parameter FormationDTO, it worked in others controllers.
FormationsController
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(FormationDTO formation)
{
if (!ModelState.IsValid){
return View("FormationForm", new FormationFormViewModel { FormationDTO = formation, Categories = GetCategories() });
}
else{
// DO THE STUFF
}
}
FormationForm.cshtml
#model BSS_IT_Education.Models.FormationFormViewModel
#{
ViewBag.Title = "Formation";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Save", "Formations"))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.FormationDTO.Id)
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.FormationDTO.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.EditorFor(model => model.FormationDTO.Name, new { htmlAttributes = new { #class = "form-control", #placeholder = "Entrez le nom de la formation..." } })
#Html.ValidationMessageFor(model => model.FormationDTO.Name, "", new { #class = "text-danger" })
</div>
</div>
// BUNCH OF OTHERS FORM-GROUPS
<div class="form-group">
<div class="col-md-offset-2 col-md-8">
<button type="submit" class="btn btn-success">#((Model.FormationDTO.Id == 0) ? "Sauvegarder " : "Modifier")</button>
</div>
</div>
</div>
}
If I am understanding the code correctly. It looks like you should be passing FormationFormViewModel to the controller action. Not FormationDTO.
Take a look at the generated HTML on the page. I'm guessing the name attributes on your input elements will look something like formationDTO.name, because your ViewModel is a FormationFormViewModel. But the ModelBinder on the backend is going to look for just a property name, because you are trying to build a FormationDTO.
You may need to manually create those input elements, or use a child action to get the correct ViewModel to a view that lets you use the razor #Html helpers to build the correct elements.
Or, the easier option is to make your controller action accept a FormationFormViewModel, then the ModelBinder should correctly build out the properties of the FormationDTO you want.
So I am developing my first webpage using ASP.NET MVC and I managed to create a fully working registration page which send the data to the database and stored the user. Simple.
However I didn't really like the look and feel of the element it created for me so I thought I could change it out.
Original code WORKING
<div class="form-group">
#Html.LabelFor(model => model.Firstname, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Firstname, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Firstname, "", new { #class = "text-danger" })
</div>
</div>
My new code NOT WORKING
<div class="row">
<div class="input-group bb-none">
<i class="fas fa-address-card"></i>
<input type="text" placeholder="Firstname">
</div>
</div>
I am 99.9% sure that it's a binding issue. I want the data I put into my new textbox
<input type="text" placeholder="Firstname">
To carry over the data to the model.
What's the part that binds it in the first option?
Tag helpers will resolve down to html and put the property name as both the id and name within the input. The model binder then binds to that.
#Html.TextBoxFor( m => m.Firstname, new { placeholder = "Firstname" })
Hit a strange issue where my model is not binding and shows up on the controller as null.
I have a form doing a httppost. My breakpoint in the controller is hit and the parameter I expect to be my model is null.
Looking at some example code on another page that works, I copied and pasted it and the only difference was the name of the parameter was 'model' instead of message.
View
#model Site.Models.ContactMessage
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ContactMessage</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.To, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.To, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.To, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
public ActionResult Contact()
{
return View();
}
[HttpPost]
public ActionResult Contact(ContactMessage message)
{
var m = message;
return View();
}
and it worked. I thought I must have entirely missed something about naming convention. Found you can use Bind, from reading a heap of other posts, to change the prefix like;
public ActionResult Contact([Bind(Prefix = "model")] ContactMessage message)
but that didn't work, still null. Going to rename it to model so it works and I can move on but would like to know why it's not binding if not called model.
public ActionResult Contact(ContactMessage message)
Changed back to this as above but still returns a null.
Interestingly, if I open up another MVC app, that one has whatever parameter names I want and works fine. It's using an older version of MVC 5 (not updated it yet but I will do that and see if anything happens. I don't expect it will.)
Your problem is that you model contains a property named Message and you also have a parameter named message The DefaultModelBinder reads the form values which will include message = "someTextValue" and searches for model properties that have the name message. It finds the one in you model and sets it value (all OK so far) but then it finds another one (your parameter) and tries to set the value of a complex object string value (in effect ContactMessage message = "someTextValue";) which fails so the model becomes null
Hit a strange issue where my model is not binding and shows up on the controller as null.
I have a form doing a httppost. My breakpoint in the controller is hit and the parameter I expect to be my model is null.
Looking at some example code on another page that works, I copied and pasted it and the only difference was the name of the parameter was 'model' instead of message.
View
#model Site.Models.ContactMessage
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ContactMessage</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Message, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Message, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Message, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.To, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.To, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.To, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Controller
public ActionResult Contact()
{
return View();
}
[HttpPost]
public ActionResult Contact(ContactMessage message)
{
var m = message;
return View();
}
and it worked. I thought I must have entirely missed something about naming convention. Found you can use Bind, from reading a heap of other posts, to change the prefix like;
public ActionResult Contact([Bind(Prefix = "model")] ContactMessage message)
but that didn't work, still null. Going to rename it to model so it works and I can move on but would like to know why it's not binding if not called model.
public ActionResult Contact(ContactMessage message)
Changed back to this as above but still returns a null.
Interestingly, if I open up another MVC app, that one has whatever parameter names I want and works fine. It's using an older version of MVC 5 (not updated it yet but I will do that and see if anything happens. I don't expect it will.)
Your problem is that you model contains a property named Message and you also have a parameter named message The DefaultModelBinder reads the form values which will include message = "someTextValue" and searches for model properties that have the name message. It finds the one in you model and sets it value (all OK so far) but then it finds another one (your parameter) and tries to set the value of a complex object string value (in effect ContactMessage message = "someTextValue";) which fails so the model becomes null
So I decided to make an auction house web application as my first asp.net mvc project and I cannot figure out how to pass a parameter between two views that belong to different controllers. In the first view, Details of AuctionHouseController, I have:
<a class="btn btn-default" href="#Url.Action("Create", "Auctions", new { id = Model.ItemId })">Start Auction ยป</a>
and a URL: http://localhost:2142/AuctionHouse/Details/123
And here is the Details method:
public ActionResult Details(int id)
{
var item = _auctionhDbc.Items.Find(id);
return View(item);
}
I want to pass the id part of the URL - the "123" to the view where the button leads - Create of AuctionsController, where I have:
<div class="form-group">
#Html.LabelFor(model => model.Item.ItemId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Item.ItemId, new { htmlAttributes = new { #class = "form-control", #Value = " " } })
#Html.ValidationMessageFor(model => model.Item.ItemId, "", new { #class = "text-danger" })
</div>
</div>
I want to place the "123" as the default value (#Value) of the Html Editor field. How can I do that?
Assuming you are using strongly typed views, your model for the Create view will already have the value of 123 in ItemID. The problem is, your model is of type Items, yet you are trying to use EditorFor for model.Item.ItemID.
Thus, instead of your line
#Html.EditorFor(model => model.Item.ItemId,
new { htmlAttributes = new { #class = "form-control", #Value = " " } })
if you use
#Html.EditorFor(model => model.ItemId,
new { htmlAttributes = new { #class = "form-control" } })
you will already have passed the value there. Make sure you use strongly typed views by putting:
#model YourNameSpace.Items
in the beginning of your view.