I have the following two classes:
public class Game
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public Console Console { get; set; }
}
public class Console
{
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Company { get; set; }
}
After this I added a GameController with scaffolding. Now I am working on the Create view of the Game Object and I am experiencing the problem that the MVC HTML.helpers are rendering input fields for all of the properties of a Console object instead of the just the title (which I guess is logical, given the fact that the property is an actual Console object).
I still want the user to choose a console to be taken along for the Game Object being created, so I tried solving it in the following way:
<div class="form-group">
#Html.LabelFor(model => model.Genre, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Genre, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Genre, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Console, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Console.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Console.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
I thought I would simply mention the title property of the model, but I'm still receiving input fields for all the Console properties in my Game's create view.
I understand this is simply the way MVC works. But how should I deal with something like this? I also tried things like:
Console: #Html.DropDownList("console", "All")
Of course the problem here is that the HTML form does not understand that this field is meant to be taken along as the console field for the game object being created. What is the correct solution for this problem?
EDIT:
I did the following:
Add a gameCreation viewModel:
public class GameCreation
{
public Game game { get; set; }
public SelectList Consoles { get; set; }
}
Then I built my ViewModel and passed it to view via controller:
public ActionResult Create()
{
var gc = new GameCreation();
gc.Consoles = cb.BuildList(db);
return View(gc);
}
Note that cb.BuildList(db) returns a SelectList item. Then in the view I tried:
<div class="form-group">
#Html.LabelFor(model => model.Console.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("console", gc.Consoles)
#Html.ValidationMessageFor(model => model.Console.Title, "", new { #class = "text-danger" })
</div>
</div>
This does not work, as gc is not known. I also tried passing the data via the ViewBag but this item is also not known here. Also I then received the error:
HTMLHelper has no applicable method named 'dropdownlist'. How can I access the data of the viewmodel which I passed?
Related
Pardon my inexperience, I've got C# chops but I'm just starting to learn ASP.NET MVC framework.
So I've got some models that look like this:
public abstract class Movement
{
public int Id { get; set; }
public string Name { get; set; }
public abstract MovementType Type { get; }
}
public class WeightedMovement : Movement
{
public override MovementType Type
{
get
{
return MovementType.WeightBased;
}
}
public double Weight { get; set; }
}
public class LengthMovement : Movement
{
public int Distance { get; set; }
public override MovementType Type
{
get
{
return MovementType.LengthBased;
}
}
}
public enum MovementType
{
LengthBased,
WeightBased
}
In the Create view and I want to use a combo box to select the type and then have the appropriate form elements appear (right now that's just either a text box for the Weight or Distance).
Here's what the view looks like so far:
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Type, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.Type)
#Html.ValidationMessageFor(model => model.Type)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
I've only ever done web work for a hobby, never professionally so I my go to naive way to do this would be to use jQuery to remove or add elements to the DOM and then casting to the correct concrete type based upon the Type property in the Controller method.
That all seems to be working around the framework instead of working with it. Are there razor tags I don't know about? Am I approaching this wrong? This obviously isn't new but I couldn't think of the right search terms for it.
I have a Company, Country and State classes.
State is related to Country
State and Country are foreign key to Company
The problem Company View shows both state and Country as separate dropdowns.
But I expect when I select State, corresponding Country should change or vice versa. Is this possible in MVC5 EF Model.
Company Class
public class Company
{
public int CompanyID { get; set; }
[Required]
[StringLength(100,MinimumLength=3)]
public string Name { get; set; }
[StringLength(200)]
public string Address { get; set; }
public int? StateID { get; set; }
public virtual State State { get; set; }
public int? CountryID { get; set;}
public virtual Country Country { get; set; }
public int CompanyTypeID { get; set; }
public virtual CompanyType CompanyType { get; set; }
}
Country Class
public class Country
{
public int CountryID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public virtual ICollection<State> States { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
State Class
public class State
{
public int StateID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public int CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
I have scaffolded and Generated the view and Company View for reference
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Company</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Address, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StateID, "StateID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("StateID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StateID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CountryID, "CountryID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CountryID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CountryID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CompanyTypeID, "CompanyTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CompanyTypeID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CompanyTypeID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div> } <div> #Html.ActionLink("Back to List", "Index")
#section Scripts { #Scripts.Render("~/bundles/jqueryval") }
Any help would be appreciated
What you need are Cascading Dropdown Lists. Here is an example from CodeProject.
http://www.codeproject.com/Articles/258172/Simple-Implementation-of-MVC-Cascading-Ajax-Drop-D
Is this possible in MVC5 EF Model.
Yes. But you'll need to do the work (or get (buy) controls that do it for you).
The limitation is not MVC, but that the default HTML controls have no concept of being dependent on another. Either write code to change the content of the State drop down when the Country drop down changes (this could include filtering the list of states by the selected country), or get thirdy party controls that do so (it is a common requirement so lots of controls out there1).
1 But Stack Overflow does not do recommendations of tools.
I am currently making a website in ASP.NET MVC where i would like to use EditorTemplates to display a List inside my ViewModel. RowViewModel has a List of RowDataViewModels. I'm trying to make a Create View for RowViewModel, where i can use
#Html.EditorFor(model => model.RowData)
and therefore i need an EditorTemplate. I created and EditorTemplate under:
Views/Shared/EditorTemplates/RowDataViewModel.cshtml
And i also tried putting the EditorTemplates folder under the /Home/ view folder, but nothing seems to work. No breakpoints are being hit inside the editortemplate. I think i did it this way before, but i might be forgetting something.
Any ideas?
RowViewModel:
public class RowViewModel
{
[Required]
[Display(Name="Name")]
public string Name { get; set; }
public List<RowDataViewModel> RowData { get; set; }
}
RowDataViewModel:
public class RowDataViewModel
{
public string Name { get; set; }
public string Value { get; set; }
public string DataType { get; set; }
}
EditorTemplate - RowDataViewModel.cshtml:
#using iDealWebRole.ViewModels
#model RowDataViewModel
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Value, "", new { #class = "text-danger" })
</div>
</div>
The problem is here: #Html.EditorFor(model => model.RowData)
You should use your editor for every row in RowData, like:
#for (int i = 0; i < Model.RowData.Count(); i++)
{
#Html.EditorFor(model => Model.RowData[i])
}
I'm almost loosing my head trying to use the default asp.net MVC features to acomplish this mission. First of all i'll introduce what i've so far and what is my goal.
I have those two models.
public class ClassifiedViewModel
{
public int? ClassifiedId { get; set; }
public List<ClassifiedWishListViewModel> wishListModel {get; set;}
[Required]
public String UserId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
}
public class ClassifiedWishListViewModel
{
public string KeyWord { get; set; }
public int CategoryId { get; set; }
public int SubCategoryId { get; set; }
}
Added more information
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ClassifiedId, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.UserId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-3">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.Description, new { #class = "form-control", rows = "10", cols = "60" })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" id="wishList">
</div>
<button type="button" class="btn" id="btnAddWish">
<span>Add item</span>
</button>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default" id="btnSave">Save</button>
</div>
</div>
</div>
}
script
$("#btnAddWish").on("click", function () {
$.get('/Classified/ClassifiedWishList', function (template) {
$("#wishList").append(template);
});
});
Controller - method to get partial view
[HttpGet]
public ActionResult ClassifiedWishList()
{
var model = new ClassifiedWishListViewModel();
var categoriesBO = new CategoriesBO();
var categories = categoriesBO.GetAllCategories();
model.Categories =
from c in categories
select new SelectListItem
{
Text = c.Name,
Value = c.CategoryId.ToString()
};
return PartialView("ClassifiedWishList", model);
}
And i'm trying to create a view to edit the Classified. But the classifiedwishList is a variable lenght property. So, i've created a partial view to that generates the ClassigiedWishListViewModel editor many times in the same form. But when i try to submit the entire form the ClassifiedWishListViewModel never gets binded to my original ClassifiedViewModel property.
Is there a way that i can acomplish that by simple using Asp.net MVC features? Every place i look suggests me to use a javascript framework and send all the information to the Controller through serialized jsons, but by using that i will loose all the validation that the Annotations performs (like required, maxlenght, others...) in the MVC, correct?
It's really frustrating that if you need a more dynamic page on Asp.net MVC you have to forget all the tools the framework offers you and start to just send and retrieve data through JSON communication.
Any kind of help is welcome! Thanks guys!
I'm new to MVC5/C# (fresh off a Silverlight project) and have a web application (not ASP.net) that I'm working on. I can't figure out how to get the value from a dropdown list that is populated from a ViewBag and not the model. Everything I've seen is geared towards ASP.NET and/or populating the dropdown from the model.
I have this model for shifts:
public class Shift
{
public Guid ShiftID { get; set; }
public string AreaOfOperation { get; set; }
public string UserName { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
And this for AreaOfOperations:
public class AreaOfOperations
{
public Guid AreaOfOperationsID { get; set; }
public String AreaOfOperation { get; set; }
public bool InUse { get; set; }
}
The relevant controller code, which populates the view nicely with a working dropdown:
public ActionResult Create(DateTime? datetime)
{
List<AreaOfOperations> list = db.AreaOfOperations.Where(i => i.InUse == true).OrderBy(aoo => aoo.AreaOfOperation).ToList();
ViewBag.DropDownAOOs = new SelectList(list, "AreaOfOperationsID", "AreaOfOperation");
Shift shift = new Shift();
shift.ShiftID = Guid.NewGuid();
shift.StartTime = DateTime.Now;
shift.UserName = User.Identity.Name;
return View(shift);
}
// POST: Shifts/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ShiftID,AreaOfOperations,UserName,StartTime")] Shift shift)
{
try
{
if (ModelState.IsValid)
{
shift.ShiftID = Guid.NewGuid();
db.Shifts.Add(shift);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(shift);
}
And my view:
#model CRMgr5.Models.Shift
#{
ViewBag.Title = "Start Shift";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Shift</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.AreaOfOperations, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AreaOfOperation", ViewBag.DropDownAOOs as SelectList, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="btnStartShift" type="submit" value="Start Shift" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Any help would be greatly appreciated. Thanks.
In the drop down list you named your select as "AreaOfOperation" but the model property is called "AreaOfOperations." Hence the binder will not be able to bind it.
As someone here already suggested you should use strongly typed html helpers such as DropDownListFor:
#Html.DropDownListFor(m => m.AreaOfOperations, ViewBag.DropDownAOOs as SelectList)
You did it for the label not sure why you opted not to use it when generating a drop down list?
I just recreated the whole thing and it worked fine
I removed the s of AreaOfOperations in your Bind Attribute
[Bind(Include = "ShiftID,AreaOfOperation(s),UserName,StartTime")]
As far as i know, you can remove this parameter attribute alltogether.
This is only used when you only want to bind to certain Attributes of your view model.
However there was one mistake: you have to repopulate the Select List if your ModelState is not valid. Otherwise your
return View(shift);
does not have the data to render a new SelectList.
Another approach is that you put the data in your ViewModel and initialize it in the default constructor. Then you dont have to worry about the data or casting.