I am learning ASP Net Core 2.2 MVC. I have read several articles regarding passing data from controller to view and vice-versa. At one point I wanted to pass more than 1 model to the view.
Then I realized that I cannot, and have to use what is called a View Model. I came up with this:
My Domain Models:
Blog.cs:
A blog can have many categories, all the other properties are the usual title, body etc.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Blogspot.Datas.Models
{
public class Blog
{
[Key]
public int id { get; set; }
[Required]
[DataType(DataType.Text)]
public string title { get; set; }
[Required]
[DataType(DataType.Text)]
public string body { get; set; }
[DataType(DataType.DateTime)]
public DateTime created_at { get; set; }
[Column(TypeName = "boolean")]
public bool comments { get; set; }
public List<Category> categories { get; set; }
}
}
Category.cs:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Blogspot.Datas.Models
{
public class Category
{
[Key]
public int id { get; set; }
[Required]
public string title { get; set; }
public int blog_id { get; set; }
[ForeignKey("blog_id")]
public Blog blog { get; set; }
}
}
In one of my view - Info.cshtml, I want to show a blog with its categories.
InfoViewModel.cs:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Blogspot.Datas.Models;
namespace Blogspot.Datas.Models.Pages
{
public class InfoViewModel
{
public InfoViewModel()
{
this.categories = new List<Category>();
this.category = new Category();
}
public int id { get; set; }
[Required]
public string title { get; set; }
[Required]
public string body { get; set; }
[Required]
public Category category { get; set; }
public List<Category> categories { get; set; }
}
}
Info.cshtml:
It shows the title and body of the blog, an its categories. I can also add a category (in the modal form).
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model Blogspot.Datas.Models.Pages.InfoViewModel
<section class="infos">
<form action="#">
<input type="hidden" asp-for="@Model.id">
<div class="form-group">
<label for="title">Title</label>
<input class="form-control" type="text" asp-for="@Model.title">
</div>
<div class="form-group">
<label for="body">Body</label>
<input class="form-control" type="text" asp-for="@Model.body">
</div>
</form>
<div class="categories">
<h3>Categories
<button type="button" style="float: right" class="btn btn-primary add-category">Add category</button>
</h3>
@foreach (var c in @Model.categories)
{
<div class="cat">
<p>@c.title</p>
<form asp-route="deleteCategory" asp-route-id="@c.id">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
<hr>
</div>
}
</div>
</section>
<div class="modal fade category" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form asp-route="storeCategory" method="post" asp-anti-forgery="true">
<div class="form-group">
<label asp-for="@Model.category.title">Title</label>
<input class="form-control" type="text" asp-for="@Model.category.title">
<span class="text-danger" asp-validation-for="@Model.category.title"></span>
</div>
<input type="hidden" asp-for="@Model.category.blog_id" value="@Model.id">
<input type="submit" value="Save category" class="btn btn-success">
</form>
</div>
</div>
</div>
</div>
Now what has got me thinking is what would be the correct way of passing a parameter to the POST store function?
[HttpPost("categories", Name="storeCategory")]
[ExportModelState]
public async Task<IActionResult> storeCategory(Category category)
{
if (ModelState.IsValid)
{
await _context.category.AddAsync(category);
await _context.SaveChangesAsync();
TempData["success"] = true;
TempData["message"] = "Category added succesfully!";
}
return RedirectToRoute("postDetails", new { id = category.blog_id });
}
What I've done is pass in the Category Domain Model. I saw articles which said that it should be View Model that gets passed, because its not a good practice to pass around Domain Models. Now my function works perfectly, but in the instance I pass a View Model, like storeCategory(InfoViewModel infoViewModel) wouldn't the other properties id, title, property, categories be redundant? Because all I need for that function is a category object.
Please enlightened me all of this patterns and conventions used.
Thank you.