ASP.NET MVC 5 get 3 models data in 1 view - asp.net

I am trying to do small application in mvc 5. In this I have 3 models say Organisation model, Product model and Package model.
Organisation model:
namespace Own.Models
{
public class OrganisationViewModel
{
[Key]
public int OrganisationID { get; set; }
[Required(ErrorMessage = "Please enter Organisation name")]
public string OrganisationName { get; set; }
[Required(ErrorMessage = "Please enter organisation address")]
public string OrganisationAddress { get; set; }
Package Model:
namespace Own.Models
{
public class PackageViewModel
{
[Key]
public int PackageID { get; set; }
[Required(ErrorMessage = "Please enter packagename")]
public string Packagename { get; set; }
[Required(ErrorMessage = "Please enter packagedescription")]
public string PackageDescription { get; set; }
Product Model:
namespace Own.Models
{
public class ProductViewModel
{
[Key]
public int ProductID { get; set; }
[Required(ErrorMessage = "Please enter Product Name")]
public string ProductName { get; set; }
[Required(ErrorMessage = "Please enter Product Description")]
public string ProductDescription { get; set; }
Now, I need to create another model named Submission model. Here in this Submission view I want the three dropdown lists for Organisation, Product and Package. If I select Organisation dropdown by selecting item in that dropdown it should display Organisation ID, Organisation Name and Organisation Address (just it should display details, read only).
Similarly for Package and Product also it should display all details and after displaying full details in one view at bottom there should be button to save the details. Here I am not using any database. Only some static data. How to display all 3 models data in that final model view? and how to get that read only data when selecting the drop down.

You can create anonamous type to concat fields
obj =db.Org
.Where(...)
.ToList()
.Select(s => new
{ ID=s.ID,
Description = string.Format("{0}-- £{1}", s.Name,s.Description)
});
Viewbag.OrgDdl=new SelectList(obj,"ID","Description");
In you view, you can use it like this
#Html.DropDownListFor(m1 => m1.ID, ViewBag.OrgDdl as IEnumerable<SelectListItem>)
Hope this helps

The best approach to this issue is creating a View Model consisting three mentioned objects as properties. Something like:
public class MyNewViewModel
{
public OrganisationViewModel MyOrganisation { get; set; }
public PackageViewModel MyPackage { get; set; }
public ProductViewModel MyProduct { get; set; }
}
Then you could populate your properties inside MyNewViewModel constructor at first or you can populte them seperatly during any action or ajax callback. So you will have your whole related data on view.

Related

Field is required. even after filling the field - Validation

I am following this course, and the instructor added code to add records to the DB. The class that's going to be added to the DB looks like this:
public class Gig
{
public int Id { get; set; }
public ApplicationUser Artist { get; set; }
[Required]
public string ArtistId { get; set; }
public DateTime DateTime { get; set; }
[Required]
[StringLength(255)]
public string Venue { get; set; }
public Genre Genre { get; set; }
[Required]
public byte GenreId { get; set; }
}
And there's a view model that's attached to the view, to do the mapping, and it looks like this:
public class GigFormViewModel
{
[Required]
public string Venue { get; set; }
[Required]
[FutureDate]
public string Date { get; set; }
[Required]
[ValidTime]
public string Time { get; set; }
[Required]
public byte Genre { get; set; }
[Required]
public IEnumerable<Genre> Genres { get; set; }
public DateTime GetDateTime() => DateTime.Parse($"{Date} {Time}");
}
I have a create method, that gets form fields, and does the mapping from view model to the model itself, and then tries to add the records to the DB, my create action looks like this:
[Authorize]
[HttpPost]
public ActionResult Create(GigFormViewModel viewModel)
{
viewModel.Genres = _context.Genres.ToList();
if (!ModelState.IsValid)
{
return View(viewModel);
}
var gig = new Gig()
{
GenreId = viewModel.Genre,
ArtistId = User.Identity.GetUserId(),
DateTime = viewModel.GetDateTime(),
Venue = viewModel.Venue
};
_context.Gigs.Add(gig);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
On the submit, I get the viewModel's property Genres and populate it with the records from the DB, then I check for ModelState.IsValid. but it's giving me:
Genres Field is required
Although I set it just 2 lines above.
Is there something wrong am doing here? Any guidance is appreciated.
Thanks.
You don't need to add a validation data annotation to Genres property in your view model because it is not a data that is set by users but it is set by you to help your view to get a collection of Genre and populate something like a dropdown list.
Firstly, remove the [Required] attribute that decorates your Genres property in GigFormViewModel.
Secondly, refactor your action method, specially the if bloc like below:
[Authorize]
[HttpPost]
public ActionResult Create(GigFormViewModel viewModel)
{
if (!ModelState.IsValid)
{
// re-populate Genres collection only is tha data is in invalid state.
viewModel.Genres = _context.Genres.ToList();
return View(viewModel);
}
// The remainder code does not change
}

Conditionally validate collection/list item properties based on parent model value

I have the following view models
public class Step4ViewModel {
[Required(ErrorMessage="Yes/No Required")]
public bool? HaveVehicles { get; set; }
public List<Vehicle> Vehicles { get; set; }
public Step4ViewModel() {
this.Vehicles = new List<Vehicle>();
}
}
public class Vehicle {
public string Index { get; set; }
[DisplayName("Registration/Vin")]
[Required(ErrorMessage="Registration Required")]
[ValidVehicleRegistrationNumber(ErrorMessage = "Invalid Id Number")]
public string Registration { get; set; }
[Required(ErrorMessage="Make Required")]
public string Make { get; set; }
[Required(ErrorMessage="Model Required")]
public string Model { get; set; }
[Required(ErrorMessage="Year")]
public string Year { get; set; }
public Vehicle()
{
this.Index = Guid.NewGuid().ToString();
}
}
The validation works just fine. The problem is that it always works. I only want the validation to fire on vehicles in the collection when the user has indicated that they have vehicles. If HaveVehicles equals false I do not want the validation to fire.
Normally for this sort of validation I would build a custom validator in which I would only return validation errors if HaveVehicles is true. I am unable to do this here because HaveVehicles resides in a parent model which is inaccesible from the within the vehicle object.
One approach I've though of that may work is using a partial view to add/remove the collection from the DOM as null collections aren't validated. If the user selects yes ajax loads a partial view which contains a for loop that iterates over each vehicle and calls another partial view to display it.
Is there a better approach to do this or is something similar to the above my only option?

ModelState is false for relations with required fields

I have two models Invoice and Client which has one to many relation. My problem is when I try to create a new invoice the modelstate becomes false because the required fields of the Client object are not filled. I tried to set default values e.g. int property {get; set;} = 1; but this causes to override the property value when I try to retrieve the relational model. I'm new to asp.net any help would be appreciated.
Here is my Client model code:
public class Client
{
public int ClientId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public string Name { get; set; }
//.....
public List<Invoice> Invoices { get; set; }
}
Invoice Model:
public class Invoice
{
public int InvoiceId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public int ClientId { get; set; }//Foreign key
public Client client { get; set; }
//....
}
My code to save an invoice:
[HttpPost]//Save quote & redirect to edit quote
public IActionResult Create(Invoice quote)
{
ViewData["Title"] = "Create Quotation";//Set title of the view!
if (ModelState.IsValid)//If quote is validated to true.
{
_context.Invoices.Add(quote);//insert quote
_context.SaveChanges();
//Redirect to edit page to add items to the invoice
return RedirectToAction("Edit", new { id = quote.InvoiceId });
}
//...
}
My code to retrieve invoices with their client names
public IActionResult Index()
{
ViewData["Title"] = "Quotations";//Set title of the view!
//Return with array of all quotations.
return View(_context.Invoices.Include(i => i.client).Where(i => i.isQuote == true).ToArray());
}
Any hints or help would be appreciated.
Found a simpler solution:
Just add virtual keyword and GG
in Invoice Model:
public class Invoice
{
public int InvoiceId { get; set; }//Primary key
[Required]
[Display(Name = "Client/Company Name")]
public int ClientId { get; set; }//Foreign key
public virtual Client client { get; set; }
//....
}

about using "virtual ICollection..." in my model design?

I my asp.net mvc HR web apps, in my entity data model, in one page razor view, I have more than one model of data loaded and displayed, for example, in The Employee razor view, I want to display Employee data information on the top, but as the same I also want to display other ralated data with this employee such as Salary, Performance and Reveiw, all of these related data are seperated SQL table which is different EF model.
So to make it simple, one employee profile model has its ralated Salary, Performance and Review model (SQL table), so I use "virtual ICollection" in Employee Profile model.
In Employee Profile model: I have these codes:
public class EmpProfile
{
public int ID { get; set; }
[Display(Name = "Employee Name")]
public string EmpName { get; set; }
[Display(Name = "Employee Number")]
public string EmpNum { get; set; }
[Display(Name = "Employee Title")]
public string EmpTitle { get; set; }
[Display(Name = "Department")]
public virtual ICollection<PerfPlan> PerfPlans { get; set; }
public virtual ICollection<ProgReview> ProgReviews { get; set; }
public virtual ICollection<ProgEvaluation> ProgEvaluations { get; set; }
public virtual ICollection<DevelopPlan> DevelopPlans { get; set; }
}
And in Employee Performance model, I have these codes (Salary and Review model are similar as this one):
public class Performance
{
public int ID { get; set; }
public int EmpProfileID { get; set; }
[Required(ErrorMessage = "{0} is required.")]
[Display(Name = "Name*:")]
public string EmpName { get; set; }
[Required(ErrorMessage = "Please enter your Employee Number.")]
[Display(Name = "Employee No.*:")]
public string EmpNum { get; set; }
......
public virtual EmpProfile EmpProfile { get; set; }
}
Now, after I build them, in my performance contoller, I found this line code in Create method:
ViewBag.EmpProfileID = new SelectList(db.EmpProfiles, "ID", "EmpName");
and in Create view generated by the controller, the EmpProfileID dropdownlist field is generated
#Html.DropDownList("EmpProfileID", String.Empty)
Can anybody tell me why the code ViewBag.EmpProfileID = new SelectList(db.EmpProfiles, "ID", "EmpName"); is generated in Create methed?
Because all the generator knows or cares about is that EmpProfileID is a non-nullable int, so it creates a field to edit it. Since it's the id for a foreign key, it helpfully gives you a list of EmpProfile ids to choose from. If you don't want this field directly editable, you can remove it or change it to a hidden field. You just need to keep in mind that it will need to be set somehow, some way before you check ModelState.IsValid or you'll fail validation.

How to set a Foreign Key relationship manually in LINQ To SQL

I've been working through the book Pro ASP.NET MVC 2 Framework by Steven Sanderson. So far it's been phenominal... just when i think I know a decent amount I find a book that shows me just how little I know.
One of the things I know little about is how to use LINQtoSQL. In Steven's book, chapters 4-6 create a very nice little shopping cart. I went through the tutorial and got everything working. Now I want to modify the cart to use a Category table instead of storing the category name as a varchar in the Product table.
Here's the Product table object with my changes to have CategoryID as a foreign key relationship to the Categories Table.
[Table(Name="Products")]
public class Product
{
[HiddenInput(DisplayValue=false)]
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public int ProductID { get; set; }
[Required(ErrorMessage="Please enter a product name")]
[Column] public string Name { get; set; }
[Required(ErrorMessage="Please enter a description")]
[DataType(DataType.MultilineText)]
[Column] public string Description { get; set; }
[Required]
[Range(0.01, double.MaxValue, ErrorMessage="Please enter a positive price")]
[Column] public decimal Price { get; set; }
[Required(ErrorMessage="Please enter a category")]
[Column] public int CategoryID { get; set; }
internal EntityRef<Category> _category;
[System.Data.Linq.Mapping.Association(ThisKey = "CategoryID", Storage = "_category")]
public Category Category {
get { return _category.Entity; }
internal set { _category.Entity = value; CategoryID = value.CategoryID; }
}
[Column] public byte[] ImageData { get; set; }
[ScaffoldColumn(false)]
[Column] public string ImageMimeType { get; set; }
And here is my Category class
[Table(Name="Categories")]
class Category
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
internal int CategoryID { get; set; }
[Column]
public int ParentCategoryID { get; set; }
[Column]
[Required]
public string Name { get; set; }
}
When I tried to build this code, i got an error that I don't understand:
Inconsistent accessibility: property type 'SportsStore.Domain.Entities.Category'
is less accessible than property 'SportsStore.Domain.Entities.Product.Category'
What does that mean / How would I fix this?
Your class "Categroy" is less visibly then "Product". "Product" has a public Property "Category" which is public. This is the "Inconsistent accessibility".
You have to declare your class "Category" public like "Product"

Resources