Save MVC dropdownlist item in database using ViewModel - c#

I am completely new to the MVC architecure and need help with saving a dropdownlist chosen item in a database. Basically for every Project Model on its Create View I need to have a Dropdown list in which I need to pick an istance of Client (Name of client is displayed in the Dropdonwlist). Based on the chosen client the ClientID should be linked to Project object instance in database upon Post method (as a foreign key).
GET part to display Create and populate Dropdownlist works, POST is what is torturing me.
This is my Project Model:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
public string Budget { get; set; }
public string BusinessCase { get; set; }
[DataType(DataType.Date)]
public string StartDate { get; set; }
[DataType(DataType.Date)]
public string FinishDate { get; set; }
public int ClientId { get; set; }
public Client Client { get; set; }
public ICollection<ProjectMember> ProjectMembers { get; set; }
public Project()
{
}
}
This is my Client Model:
public class Client
{
[Key]
public int ClientId { get; set; }
public string Name { get; set; }
public string State { get; set; }
public string Address { get; set; }
public int VAT { get; set; }
public ICollection<Project> Projects { get; set; }
public Client()
{
}
}
This is my CreateProject ViewModel (some of the attributes might be not needed - I just tried a lot of approaches already...)
public class CreateProjectViewModel
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
public string Budget { get; set; }
public string BusinessCase { get; set; }
[DataType(DataType.Date)]
public string StartDate { get; set; }
[DataType(DataType.Date)]
public string FinishDate { get; set; }
public int ClientId { get; set; }
public Client Client { get; set; }
public ICollection<ProjectMember> ProjectMembers { get; set; }
public IEnumerable<SelectListItem> Clients { get; set; }
}
This is my Controller GET method for Create (To populate the dropdownlist with Select values from Client table):
public IActionResult Create()
{
var clients = _context.Client.Select(r => r.Name);
var viewmodel = new CreateProjectViewModel
{
Clients = new SelectList(clients)
};
return View(viewmodel);
}
And Finally, this is is the Create View using the CreateProjectViewModel:
#model ProjectPortfolioApp.ViewModels.CreateProjectViewModel
#{
ViewData["Title"] = "Create";
}
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
#Html.LabelFor(model => model.Client.Name, htmlAttributes: new { #class = "control label col-md-2" })
**#Html.DropDownListFor(model => model.ClientId, Model.Clients, "---Select Client---", new { #class = "form-control" })**
</div>
The above code works fine for displaying the Dropdown elements as expected (Name of the client) using public IEnumerable<SelectListItem> Clients { get; set; }. What I am struggling with is the POST part where I receive error Object reference not set to an instance of an object. When I post model => model.ClientId If I pass here basically anyhing else e.g model => model.Name there is no error only the Client ID is not succesfully posted in database (obviously).
Here is the snippet of HTTP Post which is most probably wrong:
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(CreateProjectViewModel model)
{
if (ModelState.IsValid)
{
var project = new Project() { Name = model.Name, ClientId = model.ClientId, Budget = model.Budget, BusinessCase = model.BusinessCase, StartDate = model.StartDate, FinishDate = model.FinishDate };
_context.Add(project);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View();
}
Can anyone look at my code and help me identify:
Is my GET Create constructed fine to serve the purpose of POST Create
What should be passed in #Html.DropDownListFor as valid parameters to
avoid the error?
What should the HTTP Post Create look like?
I looked in dozens of threads, unfortunately nothing helped. Really any help would be appreciated. Thanks.

you have to fix the action
public IActionResult Create()
{
var clients = _context.Client
.Select(r => new SelectListItem{ Value= r.ClientId.ToString(), Text= r.Name)
.ToList();
var viewmodel = new CreateProjectViewModel
{
Clients = clients
.... another properties
};
return View(viewmodel);
}
and view
#Html.DropDownListFor(model => model.ClientId, #Model.Clients, "---Select Client---", new { #class = "form-control" })

Related

Validation Exception on select list of entities that have already been validated

I've been struggling with this all day
this is my first ever post so apologizes if its difficult to understand or not formatted well
I'm using a ViewModel to combine two entities in one view. for the first entity im using textboxfor & lablefor html helpers to gather data for a post request.
For the second entity Im using a dropdownlistfor which is a selectlist of a group of address objects which are entered in another view.
i have used data annotations on my address class to make all fields required.
once entries have passed validation and saved to my database via the add address view. when i try to select it with a view model i get validation exception indicating that the fields are required on my address entries
if i remove validation on the address class it seems to work
my controller
public ActionResult AddMember()
{
var ViewMember = new MemberViewModel();
ViewMember.AddressList = new SelectList(_context.Addresses, "Id", "streetName");
return View(ViewMember);
}
[HttpPost]
public ActionResult AddMember(MemberViewModel Entry)
{
var entry = new Member();
entry = Entry.member;
entry.Address = new Address() {Id= Entry.addressId };
if (ModelState.IsValid)
{
repository.AddMember(entry, _context);
TempData["message"] = "Your Member was added";
return RedirectToAction("Members", new { id = entry.Id });
}
return View();
}
my view
#model AdesinaWebApp.ViewModels.MemberViewModel
#{
ViewBag.Title = "AddMember";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>AddMember</h2>
#using (Html.BeginForm()){
#Html.LabelFor(fn => fn.member.Name)<br>
#Html.TextBoxFor(fn => fn.member.Name)<br /><br />
#Html.LabelFor(fn => fn.member.LastName)<br>
#Html.TextBoxFor(fn => fn.member.LastName)<br />
#*#Html.LabelFor(fn => fn.address.DoorNumber)<br>
#Html.TextBoxFor(fn => fn.address.DoorNumber)<br /><br />
#Html.LabelFor(fn => fn.address.streetName)<br>
#Html.TextBoxFor(fn => fn.address.streetName)<br /><br />
#Html.LabelFor(fn => fn.address.PostCode)<br>
#Html.TextBoxFor(fn => fn.address.PostCode)<br /><br />*#
#Html.LabelFor(m=>m.addressId)
#Html.DropDownListFor(m=>m.addressId,Model.AddressList,"please select your address")
<button type="submit">Submit</button>
}
my view model
public class MemberViewModel
{
public int addressId { get; set; }
public Address address { get; set; }
public Member member { get; set; } = new Member();
public SelectList AddressList { get; set; }
}
my address class
public class Address
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Please enter a Door Number")]
public int DoorNumber { get; set; }
[Required(ErrorMessage = "Please enter a Street Name.")]
public string streetName { get; set; }
[Required(ErrorMessage = "Please enter a Post Code.")]
public string PostCode { get; set; }
public ICollection<Member> Members { get; set; }
}
public class Member
{
[Key]
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string LastName { get; set; }
public Address Address { get; set; }
}
your help will be much appreciated

Select Tag Helper ASP .NET CORE 2.0 is not setting selected value

I am trying to use select tag helper in ASP .NET Core 2.0 MVC application according to this thread Select Tag Helper in ASP.NET Core MVC .
Here is my view model :
public class SensorEditViewModel
{
public int SensorID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Uid { get; set; }
public string AE_SN { get; set; }
public DateTime SubscriptionEndDate { get; set; }
public string Type { get; set; }
public bool sensorAccessApproved { get; set; }
public string UserId { get; set; }
public SelectList UserEmailList { get; set; }
}
Here is part of GET where model is filled with propper SelectList and UserId and pushed to the view:
var users = _context.Users.OrderBy(u => u.UserName).Select(x => new { Text = x.Email, Value = x.Id });
model.UserEmailList = new SelectList(users, "Value", "Text");
model.Id = ownerId;
return View(model);
and finally in the Edit view I am using Select TagHelper like this :
<div class="form-group">
<label class="control-label">User Email</label>
<select asp-for="Id" asp-items="#Model.UserEmailList" class="form-control" ></select>
</div>
But my problem is that Select is not preset with value selected in controller (model.Id = ownerId;). Do you have any clue what might be wrong? Thanks.
You need to pass the selected value into the SelectList constructor:
model.UserEmailList = new SelectList(users, "Value", "Text", ownerId)
More info in the MS docs

MVCCheckBoxList catering for a 1 to Many relationship

I have a class for which has a 1 to many relationship with another class. for this I will use class Car and class Gears. I need to create a form, which registers a car and the user needs to specify a choice of gears.
public class Car
{
public int id { get; set; }
public string desc { get; set; }
public List<Gear> Gears { get; set; }
}
public class Gear
{
public int gid { get; set; }
public int gname { get; set; }
}
using asp.net MVC 5, I have a create form, which I have scaffolded to the Car model, and within the form, I wish to have a checkboxlist of gears,
I also have a ViewModel that I have provided for my checkboxlist which is as below:
public class GearsViewModel
{
public Gear _gear {get; set; }
public bool _isChecked {get; set;}
}
Controller looks like:
Gears fetched from db context will be
"GearR","Gear1","Gear2","Gear3","Gear4","Gear5","Gear6","Gear7"
public action Create()
{
ViewBag.Gears = new SelectList(db.Gears, "gid","gname");
List<GearViewModel> _gears= new List<GearViewModel>();
foreach(Gear G in ViewBag.Gears)
{
_gears.Add(new GearViewModel(G, false));
}
ViewBag.GearsCheckList = _gears.ToList();
return View();
}
Now, this is the part I'm getting stuck at, is how to display and capture details in the CreateView.
I need assistance on how to design the Create form and how I will capture the info.
Firstly, view models should not contain data models when editing. You view models should be (add validation and display attributes as appropriate)
public class CarVM
{
public int? ID { get; set; }
public string Description { get; set; }
public List<GearVM> Gears { get; set; }
}
public class GearVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
and the GET method will be
public ActionResult Create()
{
var gears = db.Gears;
CarVM model = new CarVM
{
Gears = gears.Select(x => new GearVM
{
ID = x.gid,
Name = x.gname
}).ToList()
};
return View(model);
}
and the view will then be
#model CarVM
....
#using (Html.BeginForm())
{
..... // elements for editing ID and Description properties of CarVM
#for (int i = ; i < Model.Gears.Count; i++)
{
<div>
#Html.HiddenFor(m => m.Gears[i].ID)
#Html.HiddenFor(m => m.Gears[i].Name) // include if your want to get this in the POST method as well
#Html.CheckboxFor(m => m.Gears[i].IsSelected)
#Html.LabelFor(m => m.Gears.IsSelected, Model.Gears[i].Name)
</div>
}
<input type="submit" .... />
}
Then in the POST method
public ActionResult Create(CarVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// To get the ID's of the selected gears
IEnumerable<int> selected = model.Gears.Where(x => x.IsSelected).Select(x => x.ID);
// Initialize your data models, save and redirect
}

Displaying data from multiple models on one view (images and text)

Hi everyone so I am trying to create an application using asp.net mvc with a code first database that allows the users to be able to create a blog post with as many images as they wish.I have the data stored in the database but I I am currently trying to have the the head, body and images displaying in the display view this is what I would like it to look like : http://imgur.com/a/IR19r but I am not sure how to accomplish this. I am able to display the head and body but cannot get the images from the image table here is the database diagram: http://imgur.com/a/lvwti
Currently this is the error I get when i add this to the view #Html.DisplayFor(modelItem => item.Images)
An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' occurred in EntityFramework.SqlServer.dll but was not handled in user code
Additional information: An error occurred while executing the command definition. See the inner exception for details.
Model
public partial class PostModel
{
public PostModel()
{
Images = new List<ImageModel>();
}
[Key]
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Required(ErrorMessage = "Heading is Required")]
[Display(Name = "Heading")]
public string Heading { get; set; }
[Required(ErrorMessage = "Body is Required")]
[DataType(DataType.MultilineText)]
[Display(Name = "Body")]
public string Body { get; set; }
public virtual ICollection<ImageModel> Images { get; set; }
public IEnumerable<HttpPostedFileBase> File { get; set; }
}
public class ImageModel
{
[Key]
public int ID { get; set; }
public string Path { get; set; }
public virtual PostModel Post { get; set; }
public string DisplayName { get; set; }
}
public class ImageVM
{
public int? ID { get; set; }
public string Path { get; set; }
public string DisplayName { get; set; }
public bool IsDeleted { get; set; }
}
public partial class PostVM
{
public PostVM()
{
Images = new List<ImageVM>();
}
public int? ID { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
public IEnumerable<HttpPostedFileBase> Files { get; set; }
public List<ImageVM> Images { get; set; }
}
DbContext
public class EFDbContext : DbContext
{
public DbSet<PostModel> Posts { get; set; }
public DbSet<PostVM> PostVMs { get; set; }
public DbSet<ImageModel> Images { get; set; }
public DbSet<ImageVM> ImageVMs { get; set; }
}
Controller
public ViewResult Display()
{
return View(repository.Posts)
}
View
#model IEnumerable<Crud.Models.PostModel>
#{
ViewBag.Title = "Index";
}
#foreach (var item in Model)
{
<div>
#Html.DisplayFor(modelItem => item.Heading)
</div>
<div>
#Html.DisplayFor(modelItem => item.Body)
</div>
<div>
#Html.DisplayFor(modelItem => item.Images)
#*<img class="img-thumbnail" width="150" height="150" src="/Img/#item.Images" />*#
</div>
}
Here is alternative controller I tried but am not using as I got this error when i tried let Images = i.Path and wasn't really sure if this was meant to be how it was done
Cannot implicity convert typeCrud 'string' to 'System.Collections.Generic.List Crud.Models.ImageVm '
public ViewResult Display()
{
IEnumerable<PostVM> model = null;
model = (from p in db.Posts
join i in db.Images on p.ID equals i.Post
select new PostVM
{
ID = p.ID,
Heading = p.Heading,
Body = p.Body,
Images = i.Path
});
return View(model);
}
item.Images is a collection. So loop through that and display the images.
<div>
#foreach(var image in item.Images)
{
<img src="#image.Path" />
}
</div>
You need to make changes to the src property depending on what value you store in the Path property of image.
You can correct your other action method like this
public ViewResult Display()
{
var posts = db.Posts.Select(d => new PostVM()
{
ID = d.ID ,
Heading = d.Heading,
Body = d.Body,
Images = d.Images.Select(i => new ImageVM() { Path = i.Path,
DisplayName = i.DisplayName }
).ToList()
}).ToList();
return View(posts);
}
Now since you are returning a list of PostVM, make sure your Display view is strongly typed to that.
#model List<PostVM>
<h1>Posts</h1>
#foreach(var p in Model)
{
<h3>#p.Heading</h3>
<p>#p.Body</p>
#foreach(var image in item.Images)
{
<img src="#image.Path" />
}
}
Also, there is no point in keeping the view model classes on your db context. Keep only your entity models. View models are only for the use of UI layer.
public class EFDbContext : DbContext
{
public DbSet<PostModel> Posts { get; set; }
public DbSet<ImageModel> Images { get; set; }
}

MVC 4 Simple Populate DropDown from database model

I feel a bit stupid.
I'm trying to get a hang of MVC 4, using boxing as a functional example.
I have WeightCategories in the database (Heavyweights, etc), and Boxers.
Seem simple. The relation is a boxer has a current weight category, but when I edit, I want it to be able to change it with a drop down.
I understand how to do it if it's a list I've made myself in the code, but I have problem understanding how to "load" the list from the WeightCategory table and show it in the view/model of the boxer.
So, here is my code for the WeightCategory item:
[Table("WeightCategories")]
public class WeightCategory
{
[Key]
public int WeightCategoryId { get; set; }
public WEIGHT_CATEGORIES WeightCategoryType { get; set; }
[Display(Name = "Weight Category Name")]
[Required]
[MinLength(5)]
public string Name { get; set; }
[Display(Name = "Weight Limit In Pounds")]
public int? WeightLimit { get; set; }
}
Here is the code for the boxer item
[Table("Boxers")]
public class Boxer
{
[Key]
public int BoxerId { get; set; }
public WeightCategory CurrentWeightCategory { get; set; }
[Required]
public string Name { get; set; }
public int Wins { get; set; }
public int Losses { get; set; }
public int Draws { get; set; }
public int Kayos { get; set; }
}
In the view, I'm really not sure how to tackle that, I'm pretty sure it's not automatic and I need to load the table somewhere in the controller maybe... I'm looking for best practice or something.
Something like that in the view at the end:
#Html.DropDownListFor(model => model.CurrentWeightCategory.WeightCategoryId,
new SelectList(Model.WeightCategories, "WeightCategoryId", "Name",
Model.WeightCategories.First().WeightCategoryId))
You could design a view model:
public class MyViewModel
{
public Boxer Boxer { get; set; }
public IEnumerable<SelectListItem> WeightCategories { get; set; }
}
and then have your controller action populate and pass this view model to the view:
public ActionResult Edit(int id)
{
var model = new MyViewModel();
using (var db = new SomeDataContext())
{
// Get the boxer you would like to edit from the database
model.Boxer = db.Boxers.Single(x => x.BoxerId == id);
// Here you are selecting all the available weight categroies
// from the database and projecting them to the IEnumerable<SelectListItem>
model.WeightCategories = db.WeightCategories.ToList().Select(x => new SelectListItem
{
Value = x.WeightCategoryId.ToString(),
Text = x.Name
})
}
return View(model);
}
and now your view becomes strongly typed to the view model:
#model MyViewModel
#Html.DropDownListFor(
x => model.Boxer.CurrentWeightCategory.WeightCategoryId,
Model.WeightCategories
)

Categories