I have a peculiar problem with a partial view and associated view model.
This is what the relevant function in the controller looks like:
using MyProject.ViewModels;
[ChildActionOnly]
public PartialViewResult ShowMyView(int id)
{
return PartialView(new MyModel() { ModelID = id });
}
And then I have a view model defined as follows:
namespace MyProject.ViewModels
{
[Bind(Exclude = "ModelID")]
public class MyModel: IValidatableObject
{
public MyModel()
{
Count = 1;
}
[Required]
[HiddenInput(DisplayValue = false)]
public int ModelID { get; set; }
[Required]
[Range(1, 9999)]
public int Count { get; set; }
public IEnumerable<ValidationResult> Validate(
ValidationContext validationContext)
{
if (ModelID <= 0)
yield return new ValidationResult("Model ID missing",
new[] { "ModelID" });
if (Count <= 0)
yield return new ValidationResult("Count cannot be zero",
new[] { "Count" });
}
}
}
I have a view defined in Views/Shared called ShowMyView.cshtml.
The strange thing is that when I define it as
#inherits WebViewPage
it works fine (shows the view), but when I define the actual model as well
#inherits WebViewPage<MyProject.ViewModels.MyModel>
it will not show the view and give me the error that the view cannot be found (with a list of locations it looked in, including the Views/Shared/ShowMyView.cshtml path which does exist).
This seems to happen for whichever model class I use in the project. Any clues on what I'm doing wrong here?
Try replacing:
#inherits WebViewPage<MyProject.ViewModels.MyModel>
with:
#model MyProject.ViewModels.MyModel
in the top of your ShowMyView.cshtml view. Normally the two should be equivalent but you never know. Also make sure that when you are rendering this child action you are passing the id parameter:
#Html.Action("ShowMyView", "SomeController", new { id = "123" })
Related
I managed to populate DropDownList with value from a Database in ASP.NET MVC 5. My goal is to assing one of the dropDownList's value to a specific model, and send it back to the Database. So, if i leave the default value in the dropdownlist, the data in SQL server is null, which is Okay, but if I choose an option, I get an error :
Exception thrown: 'System.InvalidOperationException' in System.Web.Mvc.dll ("There is no ViewData item of type 'IEnumerable' that has the key 'Status'."). I tried everything so far and i am opened for suggestions. Thank you !!!
In Controller :
ViewBag.Status = new SelectList(db.Status, "Id", "Name");
in View
#Html.DropDownList("Status","Select status...")
In Controller so far..
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View();
}
[HttpPost]
public ActionResult Apply(ViewModelVM vm)
{
if (ModelState.IsValid)
{
using (myDb db = new myDb())
{
var personalinfo = new PersonalInformation()
{
FirstName = vm.PersonalInformation.FirstName,
LastName = vm.PersonalInformation.LastName,
Birthdate = vm.PersonalInformation.Birthdate,
SocialStatus = vm.SocialStatus
};
ViewBag.SocialStatus = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
db.PersonalInformations.Add(personalinfo);
db.SaveChanges();
}
return View("Success");
}
return View();
}
The model:
public partial class Status
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public SocialStatus()
{
PersonalInformations = new HashSet<PersonalInformation>();
}
public int Id { get; set; }
[StringLength(20)]
public string StatusDescription { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PersonalInformation> PersonalInformations { get; set; }
}
}
The ViewModel:
public class ViewModelVM
{
...
public Status SocialStatus { set; get; }
...
}
Firstly your using a view model so include a property in your view model for the SelectList
public IEnumerable<SelectListItem> StatusList { get; set; }
Next remove the parameter for the model from the GET method (and since you don't appear to be using the value of x, that should be removed also)
[HttpGet]
public ActionResult Apply(ViewModelVM vm,int x=0)
{
myDb db = new myDb();
ViewModelVM model = new ViewModelVM()
{
StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
};
return View(model); // return the model to the view
}
Next, your dropdown is binding to a property named Status but your view model does not contain a property named status (its SocialStatus) and SocialStatus is a complex object and you cannot bind a <select> to a complex object (a <select> only posts back a single value (or array or values in the case of <select multiple>).
In addition, because your view model contains a property which is a complex object with validation attributes on its properties, ModelState will always be invalid because you do not post back a value for StatusDescription. As a result you always return the view in the POST method, and because you have not reassigned ViewBag.Status = ...., it is null, hence the error.
Remove property public Status SocialStatus { set; get; } and include
[Display(Name = "Social Status")]
[Required(ErrorMessage = "Please select a status")]
public int SocialStatus { get; set; }
an then in the view, strongly bind to your model using
#Html.LabelFor(m => m.SocialStatus)
#Html.DropDownListFor(m => m.SocialStatus, Model.StatusList, "-Please select-")
#Html.ValidationMessageFor(m => m.SocialStatus)
Then, in the POST method, if ModelState is invalid, populate the select list again before returning the view
if(!ModelState.IsValid)
{
model.StatusList = new SelectList(db.SocialStatuses, "Id", "StatusDescription");
return View(model);
}
// save and redirect
Finally, review What is ViewModel in MVC?.
How to print the result of query in View page for ASP.NET MVC?
My code is:
public ActionResult Index()
{
var list = from m in db.MenuTables
select m.MenuName;
return View(list);
}
Now what should i write to print the result of this query in View Page?
Personally, I would get in the habit of having ViewModels and then strongly typing your View, to that model.
The model will expose ONLY THE DATA you want to display. Nothing more, nothing less. So let's assume you want to display the Name, Price and some other meta data.
Pseudo-code:
//View Model
public class MenuItem
{
public string Name { get; set; }
public decimal Price { get; set; }
public bool IsVegetarian { get; set; ]
}
public class IndexViewModel
{
public IList<MenuItem> MenuItems { get; set; }
public string MaybeSomeMessage { get; set; }
}
//in Controller
public ActionResult Index()
{
// This gets the menu items from your db, or cache or whatever.
var menuItemsFromDb = GetMenuItems();
// Let's start populating the view model.
IndexViewModel model = new IndexViewModel();
// Project the results to your model.
IList<MenuItems> menuItems = null;
if (menuItemsFromDb != null)
{
model.MenuItems = (from menuItem in menuItemsFromDb
select new MenuItem() {
Name = menuItem.Name,
Price = menuItem.Price,
IsVegetarian = menuItem.IsVegetarian
}).ToList();
}
// Anything else...
model.MaybeSomeMessage = "Hi There!";
return View(model);
}
//in View
#model IndexViewModel
<h3>#Model.MaybeSomeMessage</h3>
<ul>
#foreach(var item in Model.MenuItems)
{
<li>#item.Name - $ #item.Price</li>
}
</ul>
etc..
Note I've skipped some error checking, etc.
The point: only pass what you need.
At first, you may think this is much more code than is necessary. The best answer I can suggest to that thought, is that in the long run, you'll thank yourself for getting in the habit of this because the view should only ever know about the exact data it requires.
Nothing more, nothing less. Sending the least amount of data means you have a very light and simple view which will make your support/debugging much better. Next, you'll be able to unit test your controllers with a lot more intelligence and smarts, when you get to that.
Assuming that list is an IEnumerable of strings (i.e. that MenuName is a string).
In your view, accept the model IEnumerable<string>
#model IEnumerable<string>
and then enumerate it
#foreach( string s in Model )
{
<div>
#s
</div>
}
The first thing you want to do is call ToList() or else you could possibly be executing the same SQL query multiple times.
public ActionResult Index()
{
var list = (from m in db.MenuTables
select m.MenuName).ToList();
return View(list);
}
Secondly, I wouldn't just pass up a full list like that. You should create a ViewModel. That will allow you to pass up more data later on with a smaller effort.
public ActionResult Index()
{
var model = new IndexModel();
model.Tables = db.MenuTables.ToList();
model.AnotherValue = "MENUS";
return View(model);
}
Now we are on the view, you will need to set the model and iterate the table.
#model IndexModel
<h3>#Model.AnotherValue</h3>
<ul>
#foreach( var table in Model.Tables) {
<li>#table.Name<li>
}
</ul>
public ActionResult Index()
{
var list = from m in db.MenuTables
select m.MenuName;
return View(list);
}
//In View
#model IEnumerable<ProjectName.models.MenuTables>
#foreach(var item in Model)
{
#item.Field_Name
}
Django has a very handy test client/dummy web browser that one can use in test cases to verify the correctness of HTTP responses (e.g., status codes, context/model data). It does not require you to have the web server running, as it deals directly with the framework to simulate the calls.
I'd really love an nUnit (or similar) equivalent that we can slip right into our test suites. We're working in MVC3 and 4, and want to check things like successful 301 redirects, that model validation is correct, and that ViewModel data is correct in the views.
What's the best solution for this?
ViewModel Data should be easy to check with the following:
public T GetViewModelFromResult<T>(ActionResult result) where T : class
{
Assert.IsInstanceOf<ViewResult>(result);
var model = ((ViewResult)result).Model;
Assert.IsInstanceOf<T>(model);
return model as T;
}
[Test]
public void TheModelHasTheOrder()
{
var controller = new MyController();
var result = controller.MyActionMethod();
var model = GetViewModelFromResult<MyModel>();
Assert.That(model, Is.SameAs(???));
}
As for the model validation, if you are using the out of the box .net property attributes like [Required] etc, you can be pretty sure they will work fine, and won't need testing.
To explicitly test the [Required] etc attributes on your object you will have extract the built in .net validation into another class. Then use that class in your controllers to validate your objects, instead of the Model.IsValid property on your controller.
The model validator class:
public class ModelValidator : IModelValidator
{
public bool IsValid(object entity)
{
return Validate(entity, new List<ValidationResult>());
}
public IEnumerable<ValidationResult> Validate(object entity)
{
var validationResults = new List<ValidationResult>();
Validate(entity, validationResults);
return validationResults;
}
private static bool Validate(object entity, ICollection<ValidationResult> validationResults)
{
if (entity != null)
{
var validationContext = new ValidationContext(entity, null, null);
return Validator.TryValidateObject(entity, validationContext, validationResults);
}
return false;
}
}
This could be verifiable in unit tests with the following:
public class MySampleEntity
{
[Required]
public string X { get; set; }
[Required]
public int Y { get; set; }
}
[TestFixture]
public class ModelValidatorTests
{
[Test]
public void GivenThePropertiesArePopulatedTheModelIsValid()
{
// arrange
var _validator = new ModelValidator();
var _entity = new MySampleEntity { X = "ABC", Y = 50 };
// act
var _result = _validator.IsValid(_entity);
// assert
Assert.That(_result, Is.True);
}
}
I've just learnt how to upload pictures and bring them in view Model.
Now I'm trying to Add comments to the pictures. That means a picture can have more comments.
So I created 2 Tables, called "Gallery" and "Comment". They are related by 'One to Many'..
My model looks like that..
public class GalleryEntries
{
public List Entries { get; set; }
}
public class GalleryEntry
{
public Gallery GalleryImage { get; set; }
public List Comments { get; set; }
}
And the controller looks so..
GalleryDataContext GalleryDB = new GalleryDataContext();
public ActionResult Index()
{
GalleryEntries model = new GalleryEntries();
GalleryEntries galleryentries = new GalleryEntries();
foreach (Gallery gallery in GalleryDB.Galleries)
{
GalleryEntry galleryentry = new GalleryEntry();
galleryentry.Comments = GalleryDB.Comments.Where(c => c.BildID == gallery.ImageID).ToList();
galleryentry.GalleryImage = gallery;
galleryentries.Entries.Add(galleryentry);
}
return View(model);
}
But it doesn't work. :(
It displays "Object reference not set to an instance of an object" at the line where "galleryentries.Entries.Add(galleryentry) stands.. How can I solve this problem?
I think the problem is that you don't initialize the GalleryEntries.Entries property anywhere ... so you're attempting to add galleryentry to a List that does not exist yet, hence the NullReferenceException.
You could initialize Entries in the constructor:
public class GalleryEntries
{
public IList<GalleryEntry> Entries { get; set; }
public GalleryEntries() {
Entries = new List<GalleryEntry>();
}
}
The following code works for webform view engine.
<% Model.Categories.ForEach(x => { %>
<li>#x.Name</li>
<% }) %>
I wrote the above code as below in razor view:
#Model.Categories.ForEach(x => {
<li>#x.Name</li>
})
But this doesn't work.
Can anyone suggest, Is there any way to achieve this in razor view?
Thanks in advance.
Is there any reason you need to do that?
#foreach(var x in Model.Categories) {
<li>#x.Name</li>
}
Above does the exact same thing, and is more idiomatic.
I can't see a way to output the .ForEach() delegate result using the Razor syntax. Razor expects called methods or invoked properties to return a value, which is then emitted into the view output. Because .ForEach() doesn't return anything, it doesn't know what to do with it:
Cannot explicitly convert type 'void' to 'object'
You can have the iterator index quite tersely like so:
#foreach (var item in Model.Categories.Select((cat, i) => new { Item = cat, Index = i })) {
<li>#x.Index - #x.Item.Name</li>
}
If you want to define this as an extension method, instead of an anonymous type, you can create a class to hold the Item, Index pair, and define an extension method on IEnumerable<T> which yields the items in the original enumerable wrapped in this construct.
public static IEnumerable<IndexedItem<T>> WithIndex<T>(this IEnumerable<T> input)
{
int i = 0;
foreach(T item in input)
yield return new IndexedItem<T> { Index = i++, Item = item };
}
The class:
public class IndexedItem<T>
{
public int Index { get; set; }
public T Item { get; set; }
}
Usage:
#foreach(var x in Model.Categories.WithIndex()) {
<li>#x.Index - #x.Item.Name</li>
}
Thanks for your help. I found how to implement delegates in Razor based on the following article by Phil Haack.
Templated Razor Delegates
Here is the extension code for IEnumerable:
public static HelperResult ForEachTemplate<TEntity>(
this IEnumerable<TEntity> items,
Func<RowEntity<TEntity>, HelperResult> template)
{
return new HelperResult(writer =>
{
var index = 0;
foreach (var item in items)
{
template(new RowEntity<TEntity> {
Index = index,
Value = item }).WriteTo(writer);
index++;
}
});
}
public class RowEntity<TEntity>
{
public int Index { get; set; }
public TEntity Value { get; set; }
}
View Model is :
// IEnumerable<Person>
public class Person
{
public string Name { get; set; }
}
And the use of that extension methods:
#Model.ForEachTemplate(#<p>Index: #item.Index Name: #Item.Value.Name</p>)