I have a scenario where i should create number of buttons based on no. of items on DB.
and when a button is clicked, a popup modal should appear with a submit button.
when i submit, the parameters that is selected is not passed to my post controller Activate(Code code)
Can anyone help ?
I have this model:
public class Code
{
public int CodeId { get; set; }
public string CodeName { get; set; }
public string CodeColor { get; set; }
public int PagerNo { get; set; }
}
and This is my controller:
public IActionResult Index()
{
return View(_code.Entity.GetAll());
}
[HttpPost]
public IActionResult Activate(Code code)
{
int codeId = code.CodeId;
return RedirectToAction("Index");
}
And my View:
#model IEnumerable<CoreLibrary.Entities.Code>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<div class="container">
#foreach (var item in Model)
{
<button class="btn btn-success btn-lg" data-toggle="modal" data-target="#item-#item.CodeId">
#item.CodeName
</button>
}
#foreach (var item in Model)
{
<form asp-action="Activate" asp-controller="Activation" method="post">
<div class="modal fade" id="item-#item.CodeId" tabindex="-1" role="dialog" aria-labelledby="ModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLongTitle">Are you sure?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
#item.CodeName will be activated !
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<input type="submit" class="btn btn-primary form-control" value="Submit" />
</div>
</div>
</div>
</div>
</form>
}
</div>
You can directly add hidden field which name is CodeId , that will bind to Code object during model binding :
#foreach (var item in Model)
{
<form asp-action="Activate" asp-controller="home" method="post">
<input name="CodeId" type="hidden" value="#item.CodeId"> <-- pass CodeId
<div class="modal fade" id="item-#item.CodeId" tabindex="-1" role="dialog" aria-labelledby="ModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLongTitle">Are you sure?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
#item.CodeName will be activated !
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<input type="submit" class="btn btn-primary form-control" value="Submit" />
</div>
</div>
</div>
</div>
</form>
}
See below: I have added hidden fields
#model IEnumerable<CoreLibrary.Entities.Code>
#{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<div class="container">
#foreach (var item in Model)
{
<button class="btn btn-success btn-lg" data-toggle="modal" data-target="#item-#item.CodeId">
#item.CodeName
</button>
}
#foreach (var item in Model)
{
<form asp-action="Activate" asp-controller="Activation" method="post">
#Html.HiddenFor(m => item.CodeId)
#Html.HiddenFor(m => m.CodeName)
#Html.HiddenFor(m => m.CodeColor)
#Html.HiddenFor(m => m.PagerNo)
<div class="modal fade" id="item-#item.CodeId" tabindex="-1" role="dialog" aria-labelledby="ModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLongTitle">Are you sure?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
#item.CodeName will be activated !
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<input type="submit" class="btn btn-primary form-control" value="Submit" />
</div>
</div>
</div>
</div>
</form>
}
</div>
Related
We have two buttons inside of an Editform. They do a bit of different things. But we want the Data annotation validation to work on both button click. The first button is easy, it triggers the OnValidSubmit. But the second button, although it triggers page validation, still writes to console.
Below is the class used:
using System.ComponentModel.DataAnnotations;
namespace MyApp.Client.Models
{
public class TestModel
{
[Required]
public string MyName { get; set; }
}
}
Below is the razor component which includes two buttons and one inputtext field:
#page "/test"
<h3>test</h3>
<EditForm Model="model" OnValidSubmit="PostAsync" class="mt-5">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
<div class="row w-100">
<div class="col-md-6">
<div class="float-lg-right row">
<div class="col-6 text-center">
<button type="submit" class="btn btn-success text-black w-100">Save</button>
</div>
<div class="col-6 text-center">
<button type="submit" class="btn btn-success text-black w-100" #onclick="(async ()=>await SaveAndGoToListAsync())">Save & Go To List</button>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<label for="MyName" class="col-form-label">My Name:</label>
<InputText id="MyName" class="form-control" #bind-Value="model.MyName"></InputText>
<ValidationMessage For="#(()=>model.MyName)"></ValidationMessage>
</div>
</div>
</EditForm>
#code {
private TestModel model;
public Test()
{
model = new();
}
private void PostAsync()
{
Console.WriteLine("PostAsync");
}
private async Task SaveAndGoToListAsync()
{
Console.WriteLine("SaveAndGoToListAsync");
}
}
This is because the second button has an onclick method.
#onclick="(async ()=>await SaveAndGoToListAsync())"
Here, instead of OnValidSubmit, you should use Context and Anonymous Functions, you can call the methods related to onclicks and pass the formContext to them. For validation, you can check the validity of the form for the corresponding method using Context.Validate in the corresponding method.
#page "/test"
<h3>test</h3>
<EditForm Model="model" Context="formContext" class="mt-5">
<DataAnnotationsValidator></DataAnnotationsValidator>
<ValidationSummary></ValidationSummary>
<div class="row w-100">
<div class="col-md-6">
<div class="float-lg-right row">
<div class="col-6 text-center">
<button type="submit" class="btn btn-success text-black w-100" #onclick="(()=>PostAsync(formContext))">Save</button>
</div>
<div class="col-6 text-center">
<button type="submit" class="btn btn-success text-black w-100" #onclick="(async ()=>await SaveAndGoToListAsync(formContext))">Save & Go To List</button>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<label for="MyName" class="col-form-label">My Name:</label>
<InputText id="MyName" class="form-control" #bind-Value="model.MyName"></InputText>
<ValidationMessage For="#(()=>model.MyName)"></ValidationMessage>
</div>
</div>
</EditForm>
#code {
private TestModel model;
public Test()
{
model = new();
}
private void PostAsync(EditContext formContext)
{
bool formIsValid = formContext.Validate();
if (formIsValid == false)
return;
Console.WriteLine("PostAsync");
}
private async Task SaveAndGoToListAsync(EditContext formContext)
{
bool formIsValid = formContext.Validate();
if (formIsValid == false)
return;
Console.WriteLine("SaveAndGoToListAsync");
}
}
This question already has answers here:
Unable to resolve service for type while attempting to activate
(10 answers)
Closed 1 year ago.
I'm currently facing a problem while trying to test my Create View, here's the error I'm getting
Here's my startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IClientService, ClientService>()
.AddScoped<IServiceDossier, ServiceDossier>()
.AddScoped<ISpecialiteService, SpecialiteService>()
.AddTransient<IUnitOfWork, UnitOfWork>()
.AddScoped<IDataBaseFactory, DataBaseFactory>();
services.AddControllersWithViews();
}
Here's my controller along with constructor and the two create methods. I'm not understanding the problem because everything was injected correctly in the constructor
public class CreationDossier : Controller
{
private IServiceDossier doss;
private IClientService cl;
private IAvocatService av;
public CreationDossier(IServiceDossier doss, IClientService cl, IAvocatService av)
{
this.doss = doss;
this.cl = cl;
this.av = av;
}
public ActionResult Create()
{
ViewBag.AvocatFK = new SelectList(av.GetMany(), "AvocatId", "Avocat");
ViewBag.ClientFK = new SelectList(cl.GetMany(), "CIN", "Client");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Dossier collection)
{
try
{
doss.Add(collection);
doss.Commit();
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
Here's my cs.html file
#model Domain.Dossier
#using Domain
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Dossier</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="DateDepot" class="control-label"></label>
<input asp-for="DateDepot" class="form-control" />
<span asp-validation-for="DateDepot" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Clos" /> #Html.DisplayNameFor(model => model.Clos)
</label>
</div>
<div class="form-group">
<label asp-for="Frais" class="control-label"></label>
<input asp-for="Frais" class="form-control" />
<span asp-validation-for="Frais" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AvocatFK" class="control-label"></label>
<select asp-for="AvocatFK" class="form-control" asp-items="ViewBag.AvocatFK"></select>
</div>
<div class="form-group">
<label asp-for="ClientFK" class="control-label"></label>
<select asp-for="ClientFK" class="form-control" asp-items="ViewBag.ClientFK"></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");}
}
You haven't registered IAvoatService in Startup.cs you need something like
public void ConfigureServices(IServiceCollection services)
{
// register other services as in original implementation
services.AddTransient<IAvocatService, ??AvocatServiceImplementation>()
}
Where AvocatServiceImplementation is the concrete class that implements IAvocatService.
I want to make countries using HierarchyId type so
I created a Country model class with these properties:
namespace DotNetCore5Crud.Models
{
public class Country
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public HierarchyId parentId { get; set; }
}
}
I then created an Index view which works fine:
#model IEnumerable<Category>
<partial name="_addCategory" model="new Category()" />
#{
if (!Model.Any())
{
<div class="alert alert-warning" role="alert">
there Is no Categories
</div>
}
else
{
<div class="container">
<table class="table table-sm table-hover table-striped">
<thead>
<tr class="bg-primary text-white font-weight-bold">
<th>id</th>
<th>Name</th>
<th>HierarchyId</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>#item.Id</td>
<td>#item.Name</td>
<td>#item.hierarchyId</td>
</tr>
}
</tbody>
</table>
</div>
}
}
Then, I inject a partial view to AddCountry:
#model Category
<!-- Button trigger modal -->
<button type="button" class="btn btn-sm mb-2 btn-primary" data-toggle="modal" data-target="#exampleModalCenter">
<i class="bi bi-plus-circle"></i> Add Category
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<form method="post" asp-action="Create" asp-controller="Categories">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle"><i class="bi bi-plus-circle"></i> Add Category</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="text-muted" asp-for="Name"></label>
<input type="text" asp-for="Name" class="form-control" />
<span class="text-danger" asp-validation-for="Name"></span>
</div>
<div class="form-control">
<label class="text-muted" asp-for="hierarchyId"></label>
<select class="form-control" asp-for="hierarchyId" id="hierarchyId">
#foreach (var item in ViewBag.AllCAT)
{
<option value="#item.Id">#item.Name</option>
}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
And finally, when I send data from the view, the name is sent correctly but the HierarchyId column is null like this :
enter image description here
I do not know why that is the case... I have searched a lot and have not yet found an explanation
I'd appreciate it if you can tell my why this is the case.
Model Binding can’t bind HierarchyId type automatically , so the parentId will show ‘null’ in your controller, You can get this property manually by using Request.Form["parentId"].
The <select> passes the value through the 'value' attribute in <option>.In your code,Id is int, It does not match the type of parentId,so I replaced it with parentId.
_addCategory.cshtml
#model Country
<!-- Button trigger modal -->
<button type="button" class="btn btn-sm mb-2 btn-primary" data-toggle="modal" data-target="#exampleModalCenter">
<i class="bi bi-plus-circle"></i> Add Category
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<form method="post" asp-action="Create" asp-controller="Home">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle"><i class="bi bi-plus-circle"></i> Add Category</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="text-muted" asp-for="Name"></label>
<input type="text" asp-for="Name" class="form-control" />
<span class="text-danger" asp-validation-for="Name"></span>
</div>
<div class="form-control">
<label class="text-muted" asp-for="parentId"></label>
<select class="form-control" asp-for="parentId" id="hierarchyId">
#foreach (var item in ViewBag.Country)
{
<!--change here...-->
<option value="#item.parentId">#item.Name</option>
}
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-sm btn-primary">Save</button>
</div>
</div>
</div>
</form>
</div>
Controller
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Create() {
//For the convenience of testing, I hard-coded here
ViewBag.Country = new List<Country>()
{
new Country(){ Id=1,Name= " USA ",parentId = HierarchyId.Parse("/1/") },
new Country(){ Id=2,Name= "Canada",parentId =HierarchyId.Parse("/2/") },
new Country(){ Id=3,Name= "China",parentId = HierarchyId.Parse("/3/") },
new Country(){Id=4,Name= "Japan" ,parentId = HierarchyId.Parse("/4/")}
};
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<object> Create(Country country)
{
//Model Binding can’t bind HierarchyId automatically,
//so I use ‘Request.Form’ to accept parentId, and convent parentId to string type and use PId to accept it, Then I convert PId to HierarchyId and Assign this value to parentId
var PId = Request.Form["parentId"].ToString();
country.parentId = HierarchyId.Parse(PId);
//do your stuff....
return View();
}
}
You can see the result here
I have the next view that has a view model with two objects.
#model prueba.ViewModel.InfoSolicitanteViewModel
#{
ViewData["Title"] = "CreateSolicitante";
}
<h1>CreateSolicitante1</h1>
<h4>Solicitante</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="InfoPersonal.Ocupacion" class="control-label"></label>
<input asp-for="InfoPersonal.Ocupacion" class="form-control" />
<span asp-validation-for="InfoPersonal.Ocupacion" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
<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="Solicitante.NombreSolicitante" class="control-label"></label>
<input asp-for="Solicitante.NombreSolicitante" class="form-control" />
<span asp-validation-for="Solicitante.NombreSolicitante" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Solicitante.Numero" class="control-label"></label>
<input asp-for="Solicitante.Numero" class="form-control" />
<span asp-validation-for="Solicitante.Numero" 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>
The controllers are the same auto-generated ones, with the only exception that i told the info create Get controller what view to use so that both controllers use the same view. framework version is .NET core 2.2
right now i'm trying to post the models with a submit button each, but it only sends to the controller i access from the respective index. also, if there is a way to send both models to both controllers with only one submit, i would love to know that too. Thanks in advance!
right now i'm trying to post the models with a submit button each, but
it only sends to the controller i access from the respective index.
Add asp-controller to specify which controller you want to post the data to.
<div class="row">
<div class="col-md-4">
<form asp-controller="InfoPersonals" asp-action="Create">
//...
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
<div class="col-md-4">
<form asp-controller="Solicitantes" asp-action="Create">
//...
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
if there is a way to send both models to both controllers with only
one submit, i would love to know that too.
I think you could use jquery change function to judge which input fill the value and then append corresponding action to the form:
#model InfoSolicitanteViewModel
#{
ViewData["Title"] = "CreateSolicitante";
}
<h1>CreateSolicitante1</h1>
<h4>Solicitante</h4>
<hr />
<div class="row">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="InfoPersonal.Ocupacion" class="control-label"></label>
<input asp-for="InfoPersonal.Ocupacion" class="form-control" />
<span asp-validation-for="InfoPersonal.Ocupacion" class="text-danger"></span>
</div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Solicitante.NombreSolicitante" class="control-label"></label>
<input asp-for="Solicitante.NombreSolicitante" class="form-control" />
<span asp-validation-for="Solicitante.NombreSolicitante" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Solicitante.Numero" class="control-label"></label>
<input asp-for="Solicitante.Numero" class="form-control" />
<span asp-validation-for="Solicitante.Numero" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$(document).ready(function () {
$("input[name^='Solicitante']").change(function () {
$('form').removeAttr('action');
$('form').attr('action', '/Solicitantes/Create');
});
$("input[name^='InfoPersonal']").change(function () {
$('form').removeAttr('action');
$('form').attr('action', '/InfoPersonals/Create');
});
})
</script>
}
My testing model:
public class InfoSolicitanteViewModel
{
public InfoPersonal InfoPersonal { get; set; }
public Solicitante Solicitante { get; set; }
}
public class InfoPersonal
{
public int Id { get; set; }
public string Ocupacion { get; set; }
}
public class Solicitante
{
public int Id { get; set; }
public string NombreSolicitante { get; set; }
public string Numero { get; set; }
}
Result:
Write something in the input field, and then press enter or click outside the field.It would trigger the change event.
right now i'm trying to post the models with a submit button each, but it only sends to the controller i access from the respective index. also, if there is a way to send both models to both controllers with only one submit, i would love to know that too.
You can post both forms with one button using jQuery.ajax(). First you need to give each form a unique id. Then leave only 1 submit button and also give it a unique id.
Then register a click event to listen for that button click. If that button is inside the form, it is important to prevent the default behavior of the form as well so that it doesn't submit the form when you click the button.
Here is an example of how to send requests via ajax with jQuery. I've give the forms ids of solicitantesForm and infoPersonalsForm. The button has an id of submitButton for this example:
$(document).ready(function () {
$('#submitButton').click(function (e) {
e.preventDefault();
var solicitantesFormData = new FormData(document.getElementById('solicitantesForm'));
$.ajax({
contentType: false,
type: 'POST',
cache: false,
processData: false,
method: 'POST',
url: '/Solicitantes/Create',
data: solicitantesFormData,
success: function (response) {
alert('solicitantesForm submitted');
},
error: function (response) {
alert('error submitting solicitantesForm');
}
});
var infoPersonalsFormData = new FormData(document.getElementById('infoPersonalsForm'));
$.ajax({
contentType: false,
type: 'POST',
cache: false,
processData: false,
method: 'POST',
url: '/InfoPersonals/Create',
data: infoPersonalsFormData,
success: function (response) {
alert('infoPersonalsForm submitted');
},
error: function (response) {
alert('error submitting infoPersonalsForm');
}
});
});
});
Let's say we have these simplified models:
public class Person {
public string Address {set;get;}
}
public class Student: Person {
public float Grade {set;get;}
}
public class Teacher: Person {
public string Department {set; get;}
}
Now we want to have a create page for student and teacher. my question is how we can use benefits of inheritance to create only one page for student and teacher?
I tried this:
#model Person
#{
bool isStudent = Model is Student;
}
<form asp-action="Create">
<div class="form-horizontal">
#if (isStudent)
{
<div class="form-group">
<label asp-for="((Student)Model).Grade" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="((Student)Model).Grade" class="form-control" />
<span asp-validation-for="((Student)Model).Grade" class="text-danger"></span>
</div>
</div>
} else {
<div class="form-group">
<label asp-for="((Teacher)Model).Department" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="((Teacher)Model).Department" class="form-control" />
<span asp-validation-for="((Teacher)Model).Department" class="text-danger"></span>
</div>
</div>
}
<div class="form-group">
<label asp-for="Address" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
</form>
but it does not allow asp-for="((Student)Model).Grade"
found it:
According to https://learn.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms#the-input-tag-helper
"#" character can be used to start an inline expression
So this is correct:
asp-for="#(Model as Student).Grade"
Look into this ViewModel with inheritance in ASP .NET Core discussion on github:
It suggests a solution where a subclass tag-helper surround the fields that are specific to each subclass. That fields are rendered either if the model is null or if a specific subclass of that class is passed.
Here is the example: link