I'm quite new to C#, MVC and EF and I've hit a problem I don't seem o be able to resolve.
I'm trying to update the Create.cshtml view so that it shows/lists the itemName rather than itemID where the Item Name is in a different table.
Heres parts of my code so far:
Models:
using System;
using System.Collections.Generic;
namespace CIMSTest.Models
{
public class DirectActivityItem
{
public int ID { get; set; }
public int DirectTypeID { get; set; }
public string ActivityName { get; set; }
public DateTime DateActivityCreated { get; set; }
public bool ActivityLive { get; set; }
public ICollection<DirectActivityGroup> DirectActivityGroups { get; set; }
public DirectType DirectType { get; set; }
}
}
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace CIMSTest.Models
{
public class DirectType
{
public int DirectTypeID { get; set; }
public string DirectTypeName { get; set; }
public bool DirectTypeLive { get; set; }
public ICollection<DirectActivityItem> DirectActivityItems { get; set; }
}
}
Controller (Create):
public IActionResult Create()
{
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID");
return View();
}
// POST: DirectActivityItems/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,DirectTypeID,ActivityName,DateActivityCreated,ActivityLive")] DirectActivityItem directActivityItem)
{
if (ModelState.IsValid)
{
_context.Add(directActivityItem);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID", directActivityItem.DirectTypeID);
return View(directActivityItem);
}
Create.cshtml
#model CIMSTest.Models.DirectActivityItem
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>DirectActivityItem</h4>
<hr />
<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">
<label asp-for="DirectTypeID" class="control-label"></label>
<select asp-for="DirectTypeID" class ="form-control" asp-items="ViewBag.DirectTypeID"></select>
</div>
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateActivityCreated" class="control-label"></label>
<input asp-for="DateActivityCreated" class="form-control" />
<span asp-validation-for="DateActivityCreated" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="ActivityLive" /> #Html.DisplayNameFor(model => model.ActivityLive)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
As you can see the Direct ActivityItem table contains the DirectTypeID, but on the Create page for this I want to list the DirectTypeNames from the DirectType table and not the ID as users won't know what the ID's are.
If anyone can provide any information it would be gratefully received.
Change this:
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeID");
to this:
ViewData["DirectTypeID"] = new SelectList(_context.DirectTypes, "DirectTypeID", "DirectTypeName");
You'll want your view model returning the DirectTypeID since that will be how you best resolve the relationship, but the third parameter tells the SelectList what to display for each selection.
Related
I'm quite new to ASP.NET Core MVC and I'm having trouble retrieving a DateTime value from the database into the 'Edit' razor view.
I can use the scaffolded views to create a new Activity Item and this displays correctly in the 'Index' list, and in the 'Details' view, but when I attempt to 'Edit' the entry the DateTime value doesn't pull through from the database.
I've done plenty of reading but the main thing I seem to get in search results is information about JQuery Datepickers.
Any advice on where to look, how to resolve would be very much appreciated.
Here is my model:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MISDataRepo.Models
{
[Table("Activity",Schema = "coir")]
public partial class ActivityItem
{
public ActivityItem()
{
ActivityIdentifier = new HashSet<ActivityIdentifier>();
}
[Key]
public int ActivityItemId { get; set; }
[Required(ErrorMessage = "A valid Activity Name is required.")]
[Display(Name = "Activity Name")]
[StringLength(100)]
public string ActivityName { get; set; }
[Required]
[Display(Name = "Activity Type")]
public int ActivityTypeId { get; set; }
[Required]
[Display(Name = "Date Activity Created")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DateCreated { get; set; }
[Display(Name = "Date Activity Modified")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime? DatetModified { get; set; }
[Required]
[Display(Name = "Created By (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? CreatedBy { get; set; }
[Display(Name = "Project Co-Ordinator (Employee ID)")]
[RegularExpression("^[1-9][0-9]{6}$", ErrorMessage = "A valid Employee ID is required!")]
public int? PC { get; set; }
[DefaultValue(true)]
public bool Live { get; set; }
public virtual ActivityType ActivityType { get; set; }
public virtual ICollection<ActivityIdentifier> ActivityIdentifier { get; set; }
}
}
Here is the view:
#model MISDataRepo.Models.ActivityItem
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Edit</h1>
<h4>ActivityItem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ActivityTypeId" class="control-label"></label>
<select asp-for="ActivityTypeId" class="form-control" asp-items="ViewBag.ActivityTypeId"></select>
<span asp-validation-for="ActivityTypeId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DateCreated)" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
#*<input type="hidden" asp-for="DateCreated" type="date" placeholder="Enter Date Created" value="#Model.DateCreated" />*#
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="#Html.DisplayFor(a => a.DatetModified)" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedBy" class="control-label"></label>
<input asp-for="CreatedBy" class="form-control" />
<span asp-validation-for="CreatedBy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PC" class="control-label"></label>
<input asp-for="PC" class="form-control" />
<span asp-validation-for="PC" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Live" /> #Html.DisplayNameFor(model => model.Live)
</label>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Here are the 'Edit' methods of the controller
// GET: ActivityItems/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var activityItem = await _context.ActivityItem.FindAsync(id);
if (activityItem == null)
{
return NotFound();
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
// POST: ActivityItems/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
//public async Task<IActionResult> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DateCreated,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
public async Task<IActionResult> Edit(int id, [Bind("ActivityItemId,ActivityName,ActivityTypeId,DatetModified,CreatedBy,PC,Live")] ActivityItem activityItem)
{
if (id != activityItem.ActivityItemId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(activityItem);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ActivityItemExists(activityItem.ActivityItemId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
ViewData["ActivityTypeId"] = new SelectList(_context.ActivityType, "ActivityTypeId", "ActivityTypeName", activityItem.ActivityTypeId);
return View(activityItem);
}
But when I attempt to 'Edit' the entry the DateTime value doesn't pull
through from the database.
Yes, the issue you are having with the your View is pretty obvious due to your HTML Helper atrribute that is #Html.DisplayFor and the Property you have defined within your Model ActivityItem. You are probably getting following issue.
Problem:
How To Resolve:
Either you could use ViewModel or you can redefine your property public DateTime DateCreated { get; set; } by get rid of your annotations. However, I would prefer to use ViewModel. On the other hands, use the property like asp-for="DateCreated" within your edit view and get rid of your additional HTML helper class #Html.DisplayFor. Follow the below steps.
View Model:
public class ActivityItemViewModel
{
public int ActivityItemId { get; set; }
public string ActivityName { get; set; }
public DateTime DateCreated { get; set; }
public DateTime? DatetModified { get; set; }
}
Note: While loading your Edit view you certainly doesn't require annotations so you can ommit that.
View :
In view you are using additional HTML helper class #Html.DisplayFor which is not required in this scenario. You could try as following:
#model DotNet6MVCWebApp.Models.ActivityItemViewModel
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>ActivityItem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ActivityItemId" />
<div class="form-group">
<label asp-for="ActivityName" class="control-label"></label>
<input asp-for="ActivityName" class="form-control" />
<span asp-validation-for="ActivityName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DateCreated" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DateCreated" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DatetModified" class="control-label"></label>
<input asp-for="DateCreated" class="form-control" />
<span asp-validation-for="DatetModified" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="ActivityList">Back to List</a>
</div>
Output:
I am working on a Data Entry system for storing users financial data. Each user will enter his Revenues and Expenses each in a table.
The tables were designed as follows:
Primary Key: Rev/Exp ID
Foreign Key: Organization ID
This is a sample for my models:
public class Revenue
{
[Key]
public int RevenueId { get; set; }
public int Year { get; set; }
public double Source1 { get; set; } = 0;
public double Source2 { get; set; } = 0;
public double Source3 { get; set; } = 0;
public double Source4 { get; set; } = 0;
// Foreign Key Relationship
public string OrganizationId{ get; set; }
public virtual Organization Organization{ get; set; }
}
public class Organization
{
public virtual ICollection<Revenue> Revenues { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
}
This is the DBContext:
public class AppDbContext : IdentityDbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
// Create tables in DB
public DbSet<Organization > Organization { get; set; }
public DbSet<Revenue> Revenue { get; set; }
public DbSet<Expense> Expense { get; set; }
}
Here is the Create Action in the Controller:
// GET: Revenue/Create
public IActionResult Create()
{
return View();
}
// POST: Revenue/Create
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("RevenueId,Year,Source1,Source2,...,OrganizationId")] Revenue revenue)
{
if (ModelState.IsValid)
{
_context.Add(revenue);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrganizationId"] = new SelectList(_context.OrganizationId, "Id", "Id", revenue.OrganizationId);
return View(revenue);
}
Finally, Create View:
#using Microsoft.AspNetCore.Identity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Revenue</h4>
<hr />
<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">
<label asp-for="Year" class="control-label"></label>
<input asp-for="Year" class="form-control" />
<span asp-validation-for="Year" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source1" class="control-label"></label>
<input asp-for="Source1" class="form-control" />
<span asp-validation-for="Source1" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source2" class="control-label"></label>
<input asp-for="Source2" class="form-control" />
<span asp-validation-for="Source2" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source3" class="control-label"></label>
<input asp-for="Source3" class="form-control" />
<span asp-validation-for="Source3" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source4" class="control-label"></label>
<input asp-for="Source4" class="form-control" />
<span asp-validation-for="Source4" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OrganizationId" class="control-label"></label>
<input asp-for="OrganizationId" class="form-control" value="#UserManager.GetUserId(User)"/>
<span asp-validation-for="OrganizationId" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
So, after a lot of search I was able to capture user ID with UserManager and assigning it to a hidden field, then sending it with the form. However, that did not work, the form is not submitting and no error messages are displayed neither.
Is this is a correct way of capturing user ID as a Foreign Key and how to fix the Create Action ?
You didn't really specify anything about your authentication. If you are using typical ASP.Net authentication, you can probably use User.Identity.Name, like this:
if (ModelState.IsValid)
{
revenue.UserId = User.Identity.Name
_context.Add(revenue);
...
As from .NET 6, in order to assign an attribute in a model to be Nullable the ? should be added after the name of the attribute, otherwise it is required.
The problem was that the UserId is passed but the User object is null (which should be because it is just a reference).
So the model should be:
public class Revenue
{
[Key]
public int RevenueId { get; set; }
public int Year { get; set; }
public double Source1 { get; set; } = 0;
public double Source2 { get; set; } = 0;
public double Source3 { get; set; } = 0;
public double Source4 { get; set; } = 0;
// Foreign Key Relationship
public string OrganizationId{ get; set; }
public Organization? Organization{ get; set; }
}
And the view will be as is by passing user ID in a hidden field that we got from UserManager.
I'm somewhat new to MVC and have been following along with a tutorial but it has no answers regarding my question. For my Create page, the Foreign keys are not showing up. Basically, on the Projects page I created a project, on the People page I created a person. So when I try to create a ProjectRole on the ProjectRoles page, the ProjectId and PersonId are not showing up in the drop-down menu. Down below all of my code, I have provided a screenshot of what I have tried to put into words.
My models:
public class Project
{
public int Id { get; set; }
[Required]
[MaxLength(30)]
public string Name { get; set; }
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime DueDate { get; set; }
public ICollection<ProjectRole> ProjectRoles { get; set; }
}
public class Person
{
public int Id { get; set; }
[Required]
[MaxLength(30)]
public string FirstName { get; set; }
[Required]
[MaxLength(30)]
public string MiddleName { get; set; }
[Required]
[MaxLength(30)]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public ICollection<ProjectRole> ProjectRoles { get; set; }
}
public class ProjectRole
{
public int Id { get; set; }
[Required]
public double HourlyRate { get; set; }
[ForeignKey("Person")]
public int PersonId { get; set; }
[ForeignKey("Project")]
public int ProjectId { get; set; }
[ForeignKey("AppRole")]
public int RoleId { get; set; }
}
My Controller code:
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,HourlyRate,PersonId,ProjectId,RoleId")] ProjectRole projectRole)
{
if (ModelState.IsValid)
{
_context.Add(projectRole);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(projectRole);
}
And my view code here:
#model Project2.Models.Entities.ProjectRole
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>ProjectRole</h4>
<hr />
<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">
<label asp-for="HourlyRate" class="control-label"></label>
<input asp-for="HourlyRate" class="form-control" />
<span asp-validation-for="HourlyRate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PersonId" class="control-label"></label>
<select asp-for="PersonId" class ="form-control" asp-items="ViewBag.PersonId"></select>
</div>
<div class="form-group">
<label asp-for="ProjectId" class="control-label"></label>
<select asp-for="ProjectId" class ="form-control" asp-items="ViewBag.ProjectId"></select>
</div>
<div class="form-group">
<label asp-for="RoleId" class="control-label"></label>
<input asp-for="RoleId" class="form-control" />
<span asp-validation-for="RoleId" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Screenshot of example of what I mean:
I suppose more personable to display the Person Name (and the Project Name) in the select controls, but to pass the PersonId (and ProjectId) to the Create method when click on the Create button.
Therefore, prepare the Person list (and the Project list) like below:
public IActionResult Create()
{
var persons = new List<SelectListItem>();
// Iterate through `Persons`
foreach(var p in _context.Persons)
{
persons.Add(new SelectListItem() { Value= p.Id, Text = p.FirstName+", "+p.LastName});
}
ViewBag.Persons = persons;
// Prepare the projects list (like `Persons` list above)
// ... your code here
ViewBag.Projects = persons;
return View(new ProjectRole(){ /* Add your code here to create the ProjectRole .../* });
}
And in the view:
<div class="form-group">
<label asp-for="PersonId" class="control-label"></label>
<select asp-for="PersonId" class="form-control" asp-items="ViewBag.Persons"></select>
</div>
<div class="form-group">
<label asp-for="ProjectId" class="control-label"></label>
<select asp-for="ProjectId" class="form-control" asp-items="ViewBag.Projects"></select>
</div>
For additional information see The Select Tag Helper
*NOTE: And I would recommend to create compound view model to include all required information. *
ViewModels or ViewBag?
Understanding Best Way to Use Multiple Models in ASP.NET MVC
Im extremely new to .net core and trying to teach myself a few things just for the fun of it.
I am trying to pass a list of countries through to a form from a Country Model.
Club Model
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace SoccerExchange.Models
{
public class Club
{
public int Id { get; set; }
public string ClubName { get; set; }
public string LeagueName { get; set; }
public string Standard { get; set; }
public ICollection<Advert> Adverts { get; set; }
}
}
Country Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace SoccerExchange.Models
{
public class Country
{
public int Id { get; set; }
public string CountryName { get; set; }
public ICollection<Club> Clubs { get; set; }
}
}
ViewModel
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
using SoccerExchange.Models;
namespace SoccerExchange.ViewModels
{
public class NewClubViewModel
{
public SelectList CountriesList { get; set; }
public Club Club { get; set; }
}
}
Create Function in Controller
public IActionResult Create()
{
var countries = _context.Countries.ToList();
var viewModel = new NewClubViewModel
{
CountriesList = new SelectList(countries, "Id", "CountryName")
};
return View(viewModel);
}
Create View
#model SoccerExchange.ViewModels.NewClubViewModel
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Club</h4>
<hr />
<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">
<label asp-for="Club.ClubName" class="control-label"></label>
<input asp-for="Club.ClubName" class="form-control" />
<span asp-validation-for="Club.ClubName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Club.LeagueName" class="control-label"></label>
<input asp-for="Club.LeagueName" class="form-control" />
<span asp-validation-for="Club.LeagueName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Club.Standard" class="control-label"></label>
<input asp-for="Club.Standard" class="form-control" />
<span asp-validation-for="Club.Standard" class="text-danger"></span>
</div>
<div>
#Html.DropDownListFor(m => Model.CountriesList, "--Please Select--")
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
I am almost certain that I have the Models along with the ViewModels correct. Im also almost certain that I have the create method correct. But for some reason, I can not pass through the list data for the drop down in the form.
Would anyone have any hints on what I Might be doing wrong?
Html.DropDownListFor expects the data source as the second parameter (see below).
Image of Intellisense for DropDownListFor
This article may be some help for you ASP.NET MVC Dropdown List From SelectList
You need to fix your clab class by adding Country
public class Club
{
public int Id { get; set; }
public string ClubName { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
.....
}
and fix the view
#Html.DropDownListFor(m =>m.Club.CountryId, #Model.CountriesList, "--Please Select--")
I have a model Event with a ViewModel, Controller and View.
In this case it is about the CreateView
Model
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Verbonding.Models
{
public class Event
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
public DateTime EventStart { get; set; }
public DateTime EventEnd { get; set; }
public int OrganizationId { get; set; }
public int AddressId { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public virtual Organization Organization { get; set; }
public virtual Address Address { get; set; }
public virtual Category Category { get; set; }
}
}
ViewModel
using System;
using System.Collections.Generic;
namespace Verbonding.Models.EventViewModels
{
public class CreateViewModel
{
public string Name { get; set; }
public int? CategoryId { get; set; }
public List<Category> Categories { get; set; }
public string Description { get; set; }
public string LocationName { get; set; }
public string Street { get; set; }
public string HouseNr { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public DateTime EventStartDate { get; set; }
public DateTime EventEndDate { get; set; }
}
}
Controller
I removed the actions that are not required for this question.
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Verbonding.Data;
using Verbonding.Models;
using Verbonding.Services;
using Verbonding.Models.EventViewModels;
using Microsoft.AspNetCore.Authorization;
namespace Verbonding.Controllers
{
public class EventsController : Controller
{
private readonly ApplicationDbContext _context;
private IEventData _eventData;
private IOrganizationData _organizationData;
private IAddressData _addressData;
private ICategoryData _categoryData;
private ICountryData _countryData;
public EventsController(IEventData eventData,
IOrganizationData organizationData,
IAddressData addressData,
ICategoryData categoryData,
ICountryData countryData)
{
_eventData = eventData;
_organizationData = organizationData;
_addressData = addressData;
_categoryData = categoryData;
_countryData = countryData;
}
[Authorize]
public IActionResult Create()
{
CreateViewModel model = new CreateViewModel();
var categories = _categoryData.GetChildCategories();
model.Categories = new List<Category>(categories);
return View(model);
}
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(CreateViewModel model)
{
if (ModelState.IsValid)
{
Category category = _categoryData.Get(model.CategoryId.GetValueOrDefault());
Country country = _countryData.Get(1);
_eventData.Add(model.Name, category, model.Description, model.LocationName, model.Street,
model.HouseNr, model.PostalCode, model.City, country, model.EventStartDate, model.EventEndDate);
_eventData.Commit();
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
var categories = _categoryData.GetChildCategories();
model.Categories = new List<Category>(categories);
}
return View(model);
}
}
}
View
#model Verbonding.Models.EventViewModels.CreateViewModel
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form asp-action="Create">
<div class="form-horizontal">
<hr />
<h4>Algemene gegevens</h4>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="CategoryId" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="CategoryId" asp-items="#(new SelectList(Model.Categories, "Id", "Name"))" class="form-control">
<option>Please select a Category</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<textarea asp-for="Description" class="form-control" rows="5" cols="100"></textarea>
<span asp-validation-for="Description" class="text-danger" />
</div>
</div>
<hr />
<h4>Adresgegevens Evenement Evenement</h4>
<div class="form-group">
<label asp-for="LocationName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="LocationName" class="form-control" />
<span asp-validation-for="LocationName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Street" class="col-md-2 control-label"></label>
<div class="col-md-10">
<div><input asp-for="Street" class="form-control" style="width: 200px; float: left"/></div>
<div><input asp-for="HouseNr" class="form-control" style="width: 60px; float: left; margin-left: 20px" /></div>
<span asp-validation-for="Street" class="text-danger" />
<span asp-validation-for="HouseNr" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="PostalCode" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="PostalCode" class="form-control" />
<span asp-validation-for="PostalCode" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="City" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="City" class="form-control" />
<span asp-validation-for="City" class="text-danger" />
</div>
</div>
<hr />
<h4>Tijden Evenement</h4>
<div class="form-group">
<label asp-for="EventStartDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EventStartDate" class="form-control" />
<span asp-validation-for="EventStartDate" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="EventEndDate" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="EventEndDate" class="form-control" />
<span asp-validation-for="EventEndDate" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Opslaan" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Now when I test this, the page does reload, and the fields that have been entered stay there, but there are no validation errors.
What can I do to get the validation errors on the page?
Update
I found out that the properties of the DateTime type are required by default,
so I have changed these.
In the model:
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy HH:mm}")]
public DateTime EventStart { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd-MM-yyyy HH:mm}")]
public DateTime? EventEnd { get; set; }
In the ViewModel:
public DateTime EventStartDate { get; set; }
public DateTime? EventEndDate { get; set; }
This seems to have the desired effect for these fields.
Update 2
Based on the comments from Dai I have updated the Model and ViewModel.
To test it out I started with the Name property and left the rest as it is.
In the Model:
public string Name { get; set; }
In the ViewModel:
[required]
public string Name { get; set; }
At first this did not have the desired effect. (But also no errors)
Then I changed required to Required with a capital R, and that did the trick.
I also needed to add using System.ComponentModel.DataAnnotations;, but that was nicely handled by Visual Studio.
try to create file _ViewImports.chtml into your Views folder. Then add following lines to the file:
#using Microsoft.AspNetCore.Identity
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers