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');
}
});
});
});
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.
The book Pro ASP.NET Core 3 has the the following code in its example for chapter 27:
<div class="m-2">
<h5 class="bg-primary text-white text-center p-2">HTML Form</h5>
<form asp-page="FormHandler" method="post" id="htmlform">
<div class="form-group">
<label>Name</label>
<input class="form-control" asp-for="Product.Name" />
</div>
<div class="form-group">
<label>Price</label>
<input class="form-control" asp-for="Product.Price" />
</div>
<div class="form-group">
<label>Category</label>
<input class="form-control" asp-for="Product.Category.Name" />
</div>
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<button form="htmlform" asp-page="FormHandler" class="btn btn-primary mt-2">
Sumit (Outside Form)
</button>
</div>
As you can see, the <div class="form-group>...</div> pattern is repeated four times. As a test of partials, I added the file Pages\_FormGroupPartial.cshtml:
#model FormGroupParams
<div class="form-group">
<label>#Model.LabelContent</label>
<input class="form-control" asp-for="#Model.ForString" />
</div>
In Pages\FormHandler.cshtml.cs I added the following class:
public class FormGroupParams
{
public string LabelContent;
public string ForString;
public FormGroupParams(string labelContent, string forString)
{
LabelContent = labelContent;
ForString = forString;
}
}
To test this out, I edited Pages\FormHandler.cshtml to use the new partial right after the original approach:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", "Product.Supplier.Name")' />
In the resulting page, we see that the partial did not quite generate the same code; note that the value is now Product.Supplier.Name instead of the expected Splash Dudes:
Looking at the source, we see that the original code:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
expanded into:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="Product_Supplier_Name" name="Product.Supplier.Name" value="Splash Dudes" />
</div>
Whereas our partial:
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", "Product.Supplier.Name")' />
expanded into:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="ForString" name="ForString" value="Product.Supplier.Name" />
</div>
Is there a good way to implement this partial so as to get the same output as the original code?
UPDATE 2020-09-30
PeterG suggests the following in his answer below:
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", #Model.Product.Supplier.Name)' />
This expands into the following:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="ForString" name="ForString" value="Splash Dudes" />
</div>
whereas the goal is the following:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" type="text" id="Product_Supplier_Name" name="Product.Supplier.Name" value="Splash Dudes" />
</div>
So the value is now correct. But the id and name are still off.
It will be very complex to bind a varibale to input tag helper and make it be recognized as the corresponding model field.
This will involve the source code of the input tag helper, and consider different types of input.
What I suggest is to directly add the name and id attributes after asp-for in the partial view to overwrite the original generation.
You only need to pass an extra value parameter when calling the partial view:
Here is the code:
Pages\FormHandler.cshtml:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
<partial name="_FormGroupPartial"
model='new FormGroupParams("Supplier","Product.Supplier.Name", #Model.Product.Supplier.Name)' />
Pages\FormHandler.cshtml.cs:
public class FormGroupParams
{
public string LabelContent;
public string ForString;
public string Value;
public FormGroupParams(string labelContent, string forString, string value)
{
LabelContent = labelContent;
ForString = forString;
Value = value;
}
}
Pages_FormGroupPartial.cshtml:
#model FormGroupParams
<div class="form-group">
<label>#Model.LabelContent</label>
<input class="form-control" asp-for="#Model.Value" id="#Model.ForString.Replace(".","_")" name="#Model.ForString" />
</div>
Here is the rendered html :
The author of the book, Adam Freeman, has provided an example to me via email which demonstrates solving this using a tag helper.
Add the file TagHelpers/FormGroupTagHelper.cs:
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace WebApp.TagHelpers
{
[HtmlTargetElement("form-group")]
public class FormGroupTagHelper : TagHelper
{
public string Label { get; set; }
public ModelExpression For { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "div";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.SetAttribute("class", "form-group");
var label = new TagBuilder("label");
label.InnerHtml.Append(Label ?? For.Name);
var input = new TagBuilder("input");
input.AddCssClass("form-control");
input.Attributes["type"] = "text";
input.Attributes["id"] = For.Name.Replace(".", "_");
input.Attributes["name"] = For.Name;
input.Attributes["value"] = For.Model.ToString();
output.Content.AppendHtml(label);
output.Content.AppendHtml(input);
}
}
}
Now, in Pages/FormHandler.cshtml instead of this:
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
you can use the following:
<form-group label="Supplier" for="Product.Supplier.Name"/>
Thanks Adam!
I think the issue is caused by passing "Product.Supplier.Name" as the string value into your FormGroupParams object for the partial model. Instead you need to pass the actual value of the Product.Supplier.Name property from the model.
Something like this maybe:
<partial name="_FormGroupPartial" model='new FormGroupParams("Supplier", #Model.Product.Supplier.Name)' />
Here's an approach which uses a template function instead of a partial.
The following code:
<div class="form-group">
<label>Name</label>
<input class="form-control" asp-for="Product.Name" />
</div>
<div class="form-group">
<label>Price</label>
<input class="form-control" asp-for="Product.Price" />
</div>
<div class="form-group">
<label>Category</label>
<input class="form-control" asp-for="Product.Category.Name" />
</div>
<div class="form-group">
<label>Supplier</label>
<input class="form-control" asp-for="Product.Supplier.Name" />
</div>
can be replaced with:
#{
await Template("Name", "Product.Name");
await Template("Price", "Product.Price");
await Template("Category", "Product.Category.Name");
await Template("Supplier", "Product.Supplier.Name");
}
given the following template function at the beginning of the Razor page:
#{
async Task Template(string label, string str)
{
var expr = Model.CreateModelExpression(str);
<div class="form-group">
<label>#label</label>
<input class="form-control" asp-for="#expr.Model" />
</div>
}
}
The CreateModelExpression method referenced there is defined in the page model:
public class FormHandlerModel : PageModel
{
private ModelExpressionProvider _modelExpressionProvider { get; set; }
private DataContext context;
public FormHandlerModel(DataContext dbContext, ModelExpressionProvider modelExpressionProvider)
{
context = dbContext;
_modelExpressionProvider = modelExpressionProvider;
}
public ModelExpression CreateModelExpression(string expr)
{
var dict = new ViewDataDictionary<FormHandlerModel>(ViewData);
return _modelExpressionProvider.CreateModelExpression(dict, expr);
}
...
}
Note that a ModelExpressionProvider is brought in via dependency-injection.
The above Razor page on github: link.
The above page model on github: link.
Suggestions on how to improve this are welcome!
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
My controller's Create method binds to a TestCase object:
public async Task<IActionResult> Create([Bind("Name, Description")] TestCase testCase)
and the View that does the model bind takes a ViewModel in, which is specified like so:
public class CreateTestCaseViewModel
{
public string ProjectName { get; set; }
public TestCase TestCase { get; set; }
}
My view's code (which is just a form where you input Name and Description, and submit) is:
#model CreateTestCaseViewModel
#{
ViewData["Title"] = "Create";
}
<h2>Create Test Case</h2>
<form asp-action="Create">
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="TestCase.Name" class="col-md-2 control-label">
</label>
<div class="col-md-10">
<input asp-for="TestCase.Name" class="form-control" />
<span asp-validation-for="TestCase.Name" class="text-danger"></span>
</div>
</div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="TestCase.Description" class="col-md-2 control-label"></label>
<div class="col-md-8">
#*<input asp-for="Description" class="form-control" />*#
<input asp-for="TestCase.Description" textarea class="form-control" id="Description" rows="5"/>
<span asp-validation-for="TestCase.Description" 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-default"/>
</div>
</div>
</div>
<div>
<a asp-action="Details">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
My goal is for my method to be able to take the ProjectName as an argument as well:
public async Task<IActionResult> Create([Bind("Name, Description")] TestCase testCase, string projectName)
How can I add it from the View?
Change your controller action to accept your view model and add the new parameter to the Bind list (or remove the Bind attribute entirely which will accept all fields):
public async Task<IActionResult> Create([Bind("Name, Description, ProjectName")] CreateTestCaseViewModel createTestCaseViewModel)
Then add an input for ProjectName to the view:
<input asp-for="ProjectName" type="text" />
The easiest way is to use in controller the same model as in View.
public async Task<IActionResult> Create(CreateTestCaseViewModel model)
then in View you can create #Model.EditorFor(Model.ProjectName) if you need an input of ProjectName #Model.HiddenFor(Model.ProjectName) if you need just store value from Model.
Update You can put it right after element <div class="form-horizontal">
Actually it is not matter where, this element will be hidden from the user. The only requirement it should be inside your <form asp-action="Create"> element