Hello I'm trying to make my dropdownlist to work but it seems harder than expected. I have 3 domain classes Member, Rental and Movie. My Idea was to make a dropdownlist that will show a specific users rented movies and when I select a movie in the dropdownlist and submit it I will get back the selected movie and I can set bool IsInStock to true.
So I made a viewmodel and a controller action but would like some help how to go forward with this. Now I get a dropdownlist with the users "Jan" rented movies but when I klick submit I would like to get the values back in order to set the IsInStock to true. I know I will need method to handle the POST values but I'm trying to make this work first.
public class Member
{
public virtual int MemberId { get; set; }
public virtual string LastName { get; set; }
public virtual List<Rental> Rentals { get; set; }
}
public class Rental
{
public virtual int RentalId { get; set; }
public virtual int MovieId { get; set; }
public virtual Movie Movie { get; set; }
public virtual int MemberId { get; set; }
public virtual Member Member { get; set; }
}
public class Movie
{
public virtual int MovieId { get; set; }
public virtual string Name { get; set; }
public virtual bool IsInStock { get; set; }
}
public class RentalsViewModel
{
// Need something here.
public IEnumerable<SelectListItem> RentedMovies { get; set; }
}
public ActionResult ReturnMovie()
{
var rentedmovies = db.Rentals.Where(r => r.Member.Name == "Jan").ToList();
var model = new RentalsViewModel()
{
RentedMovies = rentedmovies.Select(x => new SelectListItem
{
Value = x.MovieId.ToString(),
Text = x.Movie.Name
})
};
return View(model);
}
// In the View
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.RentedMovies, //Something here);
<p>
<input type="submit" value="Save" />
</p>
}
First parameter here:
#Html.DropDownListFor(x => x.RentedMovies, //Something here);
must be int, because you will send this value to the server:
public virtual int MovieId { get; set; }
So, your example could look like:
#Html.DropDownListFor(x => x.RentedMovieId, Model.RentedMovies);
(Add property RentedMovieId to RentalsViewModel)
Related
I am creating a filter view to find records. This example on SO helps, but does not mention how handle the (Filtered) View.
The err below is because, the actions returns a List<ProductViewModel>, and it Errors/complains that the View is using a SearchViewModel, I need to this POST the searchmodel/variables, butGET back the list/results model
The model item passed into the dictionary is of type
'System.Collections.Generic.List`1[ViewModels.ProductVM]', but this
dictionary requires a model item of type 'ViewModels.SearchModel'.
Issue/Question: Since there are two models, the SearchViewModel passed to the controller & the ProductViewModel returned as a result, which model should be strongly typed to the view? and How can I create the view to handle both SearchModel & ProductModel If I stronglyType ProductVM, then I loose the submitform from the SearchVM.
I create the SearchView as the mainview, & the _ResultsPartialView as a partialView, is this wrong?
public ActionResult Index(SearchModel searchModel)
{
var filteredProdVMList = _Repository.GetFilteredProducts(searchModel);
return View(filteredProdVMList);
}
public class ProductVM
{
public int Id { get; set; }
public int Price { get; set; }
public string Name { get; set; }
// implicit const... blah.. removed
}
public class SearchModel
{
public int? Id { get; set; }
public int? PriceFrom { get; set; }
public int? PriceTo { get; set; }
public string Name { get; set; }
}
You need to modify your SearchModel to include a collection property for the products
public class SearchModel
{
public int? PriceFrom { get; set; }
public int? PriceTo { get; set; }
....
public IEnumerable<ProductVM> Products { get; set; } // add
}
then you return just SearchModel to your view
public ActionResult Filter(SearchModel filter)
{
filter.Products = _repository.GetFilteredProducts(filter);
return View(filter);
}
and your view will be
#model SearchModel
....
#using (Html.BeginForm("Filter", "yourControllerName", FormMethod.Get))
{
#Html.LabelFor(m => m.PriceFrom)
#Html.EditorFor(m => m.PriceFrom)
#Html.ValidationMessageFor(m => m.PriceFrom)
... // other form controls for properties you want to filter the results on
<input type="submit" value="Filter" />
}
#Html.Partial("_ResultsPartialView", Model.Products)
So basically once the user clicks on the module for create, I would want it to show the module shown instead of a drop down list for selection.
#model Module1.Models.Learn
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<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>Learn</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ModuleId, "Module")
</div>
<div class="editor-field">
#Html.DropDownList("ModuleId", String.Empty)
#Html.ValidationMessageFor(model => model.ModuleId)
</div>
Above is my Create View for Learning Outcome.
#model Module1.Models.Module
#{
ViewBag.Title = "Details";
}
<h2>Learning Outcome : #Model.Code</h2>
<br /
This is the Details View for my Learning Outcome. I tried using the code "#Module.Code" to portray in my Create View but was unsuccessful. Is there another way for me to have the same result in the create view?
My Module Model
using System.Collections.Generic;
namespace Module1.Models
{
public class Module
{
public int ModuleId { get; set; }
public int CourseId { get; set; }
public int LecturerId { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public virtual Course Course { get; set; }
public virtual Lecturer Lecturer { get; set; }
public List<Reference> References { get; set; }
public List<Assessment> Assessments { get; set; }//new
public List<Front> Fronts { get; set; }
public List<Learn> Learns { get; set; }
public List<Topic> Topics { get; set; }
}
}
My Learn Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Module1.Models
{
public class Learn
{
public int LearnId { get; set; }
public int ModuleId { get; set; }
public string LearnText { get; set; }
public bool A { get; set; }
public bool B { get; set; }
public bool C { get; set; }
public bool D { get; set; }
public bool E { get; set; }
public bool F { get; set; }
public bool G { get; set; }
public bool H { get; set; }
public bool I { get; set; }
public virtual Module Module { get; set; }
}
}
In your GET method for create you need to generate a SelectList that represents the options you want to render in the dropdown. Ideally you should be using a view model to represent what you want to display/edit, but in this example, I'll just assign in to ViewBag
var modules = // call database to get all the modules
ViewBag.ModuleList = new SelectList(modules, "ModuleId", "Code");
View
#model Module1.Models.Learn
....
#using (Html.BeginForm()) {
....
#Html.DropDownListFor(m => m.ModuleID, (SelectList)ViewBag.ModuleList, "-please select-")
....
}
or if your using a view model (which you should, refer What is ViewModel in MVC?) then it would be
#Html.DropDownListFor(m => m.ModuleID, Model.ModuleList, "-please select-")
Note the value of property Module.Code will be the display text, and the value of property Module.ModuleId will be bound to the Learn.ModuleID property. If you set the value of Learn.ModuleID in the controller before passing it to the view, then the text for that Module will be displayed, otherwise the "-please select-" option will be selected.
Note ALWAYS use strongly typed helpers - DropDownListFor() not DropDownList()
Edit
Based on further comments by OP, to display the value as 'readonly'. In the controller, get the Code value for the Module and assign to a view model or ViewBag property
var module = db.Modules.Where(m => m.ModuleId == learn.ModuleId).FirstOrDefault();
if (module != null)
{
ViewBag.Code = module.Code;
}
and in the view
<div>#ViewBag.Code</div>
or
#Html.DisplayFor(m => m.Code) // if using a view model
and if you want to post back the value as well, include a hidden input for the ModuleID
#Html.HiddenFor(m => m.ModuleId)
I'm having an issue where the model is valid when it creates the HttpGet page, but invalid (some properties are null) on the HttpPost.
Here is my call to open the page:
var quote = new Quote
{
Agency = assignedAgency,
Insured = insured,
RiskAddress = insured.Address
};
db.Quotes.Add(quote);
db.SaveChanges();
return RedirectToAction("Edit", "Quote", quote.QuoteID);
And the HttpGet ActionResult - At this point, the model is valid. Insured and Agency properties are populated and have their respective Address properties populated as well.
[HttpGet]
public ActionResult Edit(int quoteID)
{
var model = db.Quotes.Find(quoteID);
return View(model);
}
And the HttpPort ActionResult - At the entry of this method, the Insured, Agency and their respective Address properties are null, causing an invalid Model state.
[HttpPost]
public ActionResult Edit(Quote model)
{
if (ModelState.IsValid)
{
if (model.SubCosts > 0)
{
model.InsuredSubs = true;
model.SubClassCode = "95626 - Subcontractor - TEST CLASS";
model.SubClassExposure = model.SubCosts;
}
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Edit", new { quoteID = model.QuoteID });
}
return View(model);
}
And the HTML:
#model DomaniOnline.Models.DomaniData.Quote
#{
ViewBag.Title = "Classification";
}
<h2>General Liability Classification</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
#Html.HiddenFor(model => model.QuoteID)
#Html.HiddenFor(model => model.Insured)
#Html.HiddenFor(model => model.Insured.Address)
#Html.HiddenFor(model => model.RiskAddress)
#Html.HiddenFor(model => model.Agency)
#Html.HiddenFor(model => model.Agency.Address)
.
.
.
.
<p>
#Html.ActionLink("Back to Insured Info", "Edit", "Insured", new { insuredID = Model.Insured.InsuredID }, null)
<input type="submit" value="Save & Continue" />
</p>
</fieldset>
}
I'd like to point out that the "Back to Insured Info" ActionLink at the bottom of the page works just fine and is able to supply the correct model.Insured.InsuredID. But for some reason, when submitting the form to the HttpPost, any property that is of a custom datatype is null. the Html.HiddenFor<> were added as a test while trying to get this to work.
edit: Classes:
public class Quote
{
public virtual int QuoteID { get; set; }
public virtual Address RiskAddress { get; set; }
public virtual Agency Agency { get; set; }
public virtual Insured Insured { get; set; }
public virtual DateTime PropEffDate { get; set; }
public virtual bool InsuredSubs { get; set; }
public virtual int SubCosts { get; set; }
.
.
.
}
public class Address
{
[Required]
public virtual string StreetAddress { get; set; }
[Required]
public virtual string City { get; set; }
[Required]
public virtual string State { get; set; }
[Required]
public virtual string ZipCode { get; set; }
}
public class Insured
{
public virtual int InsuredID { get; set; }
[Required]
public virtual string Name { get; set; }
[Required]
public virtual Address Address { get; set; }
public virtual string DBA { get; set; }
[Required]
public virtual string BusinessType { get; set; }
public virtual string Phone { get; set; }
}
Your problem is that your using HiddenFor on complex types. This only knows how to render simple types.
I would suggest either only persisting the ID's of those objects or using the Html.Serialize helper function from the MVC Futures project to serialize your account and insured objects, then deserialize them in your controller.
http://weblogs.asp.net/shijuvarghese/archive/2010/03/06/persisting-model-state-in-asp-net-mvc-using-html-serialize.aspx
Persisting the ID's should just be the InsuredID, from which you can re-get the Insured and it's address on postback. The same with Agency.
I have a class, Event, and i want to be able to have images on the event page. I have defined the image class but am now sure how i can upload the image. I want to be able to store the image in the database.
public class Event
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Address { get; set; }
public string AddressTwo { get; set; }
public virtual Person Owner { get; set; }
public DateTime Date { get; set; }
public virtual Image Image { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Name { get; set; }
public string AlternateText { get; set; }
public virtual string CssClass { get; set; }
public Byte[] File { get; set; }
}
If you want to handle file uploads you should use the HttpPostedFileBase type to represent the image and not a byte array:
public class Image
{
public int Id { get; set; }
public string Name { get; set; }
public string AlternateText { get; set; }
public virtual string CssClass { get; set; }
public HttpPostedFileBase File { get; set; }
}
then in your view you will use a file input:
#model Event
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
#Html.LabelFor(x => x.Image.File)
#Html.TextBox(x => x.Image.File, new { type = "file" })
</div>
... some other fields
<button type="submit">OK</button>
}
and finally you will have the controller action to which the form will be posted and which will save the file:
[HttpPost]
public ActionResult Upload(Event model)
{
if (model.Image != null && model.Image.ContentLength > 0)
{
// an image was selected by the user => process and store it into the database
}
...
}
You might also find the following blog post useful.
I found many articles on this but still I don´t know how exactly to do this. I am trying to create my own blog engine, I have View for create article (I am using EF and Code first) and now I must fill number of category in which article should be add but I want to change it to dropdownlist with names of categories. My model looks this:
public class Article
{
public int ArticleID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public int CategoryID { get; set; }
public DateTime Date { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public virtual Category Category { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
I know I must use Enum (or I think) but I am not exactly sure how. I don´t know which tutorial from that I found is best for me.
Edit:
Thanks for your answers but I found something else. I am trying this:
This is my model:
public class Article
{
[Key]
public int ArticleID { get; set; }
[Display(Name = "Title")]
[StringLength(30, MinimumLength = 5)]
[Required]
public string Title { get; set; }
public DateTime Date { get; set; }
public int CategoryID { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public Category Category { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required]
public string CategoryName { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
This is my controller to create article:
public ActionResult Vytvorit()
{
IEnumerable<Category> categories = GetCaregories();
var view = View(new Article() { Categories = categories });
view.TempData.Add("Action", "Create");
return view;
}
private static IEnumerable<Category> GetCaregories()
{
IEnumerable<Category> categories;
using (BlogDBContext context = new BlogDBContext())
{
categories = (from one in context.Categories
orderby one.CategoryName
select one).ToList();
}
return categories;
}
private Category GetCategory(int categoryID)
{
return db.Categories.Find(categoryID);
}
//
// POST: /Clanky/Vytvorit
[HttpPost]
public ActionResult Vytvorit(Article newArticle)
{
try
{
if (newArticle.CategoryID > 0)
{
newArticle.Category = GetCategory(newArticle.CategoryID);
}
if (TryValidateModel(newArticle))
{
db.Articles.Add(newArticle);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
newArticle.Categories = GetCaregories();
var view = View(newArticle);
view.TempData.Add("Action", "Create");
return view;
}
}
catch
{
return View();
}
}
And this is part of my view:
#Html.DropDownListFor(model => model.CategoryID, new SelectList(Model.Categories,"CategoryID","CategoryName"))
#Html.ValidationMessageFor(model => model.CategoryID)
I have problem with NullReferenceExeption but I don´t know why. Can I do it this way? It looks very easy for me.
Your model seems quite strange. It contains properties such as CategoryID and Category which seem redundant. It also contains a SelectListItem collection property called Categories. So, is this a model or a view model? It looks quite messed up. Let's assume it's a model. In this case it would more likely look something like this:
public class Article
{
public int ArticleID { get; set; }
[Required]
public string Title { get; set; }
public DateTime Date { get; set; }
[Required()]
[DataType(DataType.MultilineText)]
[AllowHtml]
public string Text { get; set; }
public virtual Category Category { get; set; }
public IEnumerable<Category> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Article> Articles { get; set; }
}
Now that the model is clear we could define a view model which will be passed to the view. A view model is a class which is specifically designed for the view. So depending on what you intend to put in this view you define it in this view model. So far you have talked only about a drop down, so let's do it:
public class ArticleViewModel
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
and then we have a controller:
public class ArticlesController: Controller
{
private readonly IArticlesRepository _repository;
public ArticlesController(IArticlesRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
Article article = _repository.GetArticle();
ArticleViewModel viewModel = Mapper.Map<Article, ArticleViewModel>(article);
return View(viewModel);
}
}
So the controller uses a repository to fetch the model, maps it to a view model (in this example I use AutoMapper) and passes the view model to the view which will take care of showing it:
#model AppName.Models.ArticleViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedCategoryId,
new SelectList(Model.Categories, "Value", "Text"),
"-- Select category --"
)
<input type="submit" value="OK" />
}
I have gone through this as well and I have to agree that at first it seems odd (In my explanation I'm assuming you want to select one category only, but the process is very similar for a multi select).
Basically you need to perform 3 steps:
1:
You need two properties on your viewmodel
One will hold the selected category id (required for postback) and the other will a SelectList with all possible categories:
public class Article
{
public int ArticleID { get; set; }
public int CategoryID { get; set; }
public SelectList Categories { get; set; }
}
2:
Also before passing the viewmodel on to the view you need to initialize the SelectList (Best practivce is to prepare as much as possible before passing a model into the view):
new SelectList(allCategories, "CategoryID", "Name", selectedCategoryID)
3:
In the view you need to add a ListBox for the CategoryID property, but using the Categories property too fill the ListBox with values:
#Html.ListBoxFor(model => model.CategoryID , Model.Categories)
Thats it! In the post back action of the controller you will have the CategoryID set. You can do whatever you need to from there to persist things in your db.