This question already has answers here:
Asp.Net MVC: Why is my view passing NULL models back to my controller?
(2 answers)
Closed 5 years ago.
I'm new to c# and asp.net MVC and i'm working on a small project. I'm having an error on a create view using a view model. Whats happening is that after I complete the form when I submit it the data I populated isn't being received by the controller. Below is the code for my view, for my create on the controller and a view of the ModelView i'm using
//Controllers
// GET: Producto/Create
public ActionResult Create()
{
return View(ProductoViewModel.CargarListas());
}
// POST: Producto/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ProductoViewModel producto)
{
Producto aux = producto.Producto;
aux.IdRecetaPreferida = producto.IdRecetaPreferida;
if (aux.Guardar())
{
return RedirectToAction(nameof(Index));
}
return View();
}
/*
* My view model im using on the view
*/
public class ProductoViewModel
{
public Producto Producto {get;set;}
[Display(Name = "Receta")]
public long IdRecetaPreferida { get; set; }
public Receta Receta { get; set; }
public List<Receta> Recetas { get; set; }
public ProductoViewModel()
{
this.Recetas = new List<Receta>();
this.Receta = new Receta();
}
public static ProductoViewModel CargarListas()
{
ProductoViewModel productoViewModel = new ProductoViewModel()
{
Recetas = Receta.TraerTodos()
};
return productoViewModel;
}
}
//My create view
#model WebApp.ViewModels.ProductoViewModel
<h2>Ingresar Producto</h2>
<hr />
<div class="row">
<div class="col-md-12">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group row">
<label asp-for="Producto.Nombre" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Producto.Nombre" class="form-control" placeholder="Nombre Producto" />
<span asp-validation-for="Producto.Nombre" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="Producto.Descripcion" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Producto.Descripcion" class="form-control" placeholder="Una rica descripcion del producto" />
<span asp-validation-for="Producto.Descripcion" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<label asp-for="IdRecetaPreferida" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
#Html.DropDownListFor(m => m.IdRecetaPreferida, new SelectList(Model.Recetas, "Id", "Nombre"), new { #class = "custom-select" })
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
<a asp-action="Index">Volver</a>
</div>
</form>
</div>
</div>
If could point out my error would appreciate it. Below is a screenshot of the variable being debugged
I believe you forgot to specify that the form should send a POST request instead of a GET (default).
<form asp-action="Create" method="post">
Or you can remove the HttpPost attribute from the Create method.
Related
This question already has answers here:
Values of disabled inputs will not be submitted
(9 answers)
Closed 1 year ago.
I am trying to learn how to pass data into an HttpPost function in Asp.Net Core MVC using tag helpers.
Here is my Model:
public class PersonModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Exists { get; set; }
}
Here is my View:
<form asp-controller="Home" asp-action="CustomerInput" method="post">
<div class="row">
<div class="col">
<div class="form-group">
<label for="lblId">
Id
</label>
<input asp-for="#Model.Id" type="text" class="form-control" id="lblId" disabled>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<label for="lblName">
Name
</label>
<input asp-for="#Model.Name" type="text" class="form-control" id="lblId" disabled>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<div class="form-check">
<input asp-for="#Model.Exists" class="form-check-input" type="checkbox" id="chkExists">
<label class="form-check-label" for="chkExists">
Exists
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-group">
<input type="submit" name="btnNext" value="Next" class="btn btn-secondary" />
</div>
</div>
</div>
Here is my Controller:
[HttpGet]
public IActionResult CustomerInput()
{
var model = new PersonModel { Id = 1, Name = "Homer Simpson", Exists = false };
return View(model);
}
[HttpPost]
public IActionResult CustomerInput(PersonModel model)
{
return View("CustomerOutput", model);
}
When I attempt to submit the form and pass the model to my HttpPost function, only the Exists value passes successfully. Id is always 0 and Name is always null.
What am I missing?
Disabled inputs won't post back to the controller. Use readonly:
<input asp-for="#Model.Id" type="text" class="form-control" id="lblId" readonly>
See devlin carnate's post for clarity
It's also good practice to use unique identifiers for your input id's!
I have a problem, where I have a form, and some of the data is not being set in the model and I don't know why or where to look.
My Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace VolunteerMngSystm.Models
{
public class Organisations
{
public int ID { get; set; }
[Required]
public string Organisation_name { get; set; }
[Required]
public string Industry { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string OrganisationsID { get; set; }
}
}
The Email and OrganisationID are not being set.
My Controller Action method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> OrgReg([Bind("Organisation_name,Industry,Email,OrganisationID")] Organisations organisations)
{
try
{
if (ModelState.IsValid)
{
_context.Add(organisations);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(OrgHome));
}
}
catch (DbUpdateException e)
{
//Log the error (uncomment ex variable name and write a log.
ModelState.AddModelError("" + e, "Unable to save changes. " +
"Try again, and if the problem persists " +
"see your system administrator.");
}
return View(organisations);
}
My View:
#model VolunteerMngSystm.Models.Organisations;
#{
ViewData["Title"] = "Register";
}
<h1>Register</h1>
<h5>As Organisation</h5>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="OrgReg">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Organisation_name" class="control-label">Organisation Name</label>
<input asp-for="Organisation_name" class="form-control" />
<span asp-validation-for="Organisation_name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Industry" class="control-label"></label>
<input asp-for="Industry" class="form-control" />
<span asp-validation-for="Industry" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OrganisationsID" class="control-label">Verification ID</label>
<input asp-for="OrganisationsID" type="file" accept="image/* " />
<span asp-validation-for="OrganisationsID" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Register" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Can anyone please advise me on where to look or how to fix this problem, please?
Since your action need AntiForgeryToken, add it to your form
<form asp-action="OrgReg">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#Html.AntiForgeryToken()
or remove it from the action
also try to remove [Bind] from the action. You have a typo there and you don't need it since you are binding all properties any way.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> OrgReg( Organisations organisations)
This is my Index Action,
I give all products to my view
public async Task<IActionResult> Index()
{
return View(await _productRepo.GetAllAsync());
}
This is my Create Action,
I selected all categories Id and Name to View.Categories
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name,SKU,Description,Price,OldPrice,Status,IsMenuItem,Count,CategoryId")] Product productModel)
{
ViewBag.Categories = new SelectList(await _categoryrepo.GetAllAsync() , "Id" , "Name");
if (ModelState.IsValid)
{
await _productRepo.CreateAsync(productModel);
return RedirectToAction(nameof(Index));
}
return View(productModel);
}
This is my view
#model OnlineShopArmenia.Models.Product
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Product</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="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SKU" class="control-label"></label>
<input asp-for="SKU" class="form-control" />
<span asp-validation-for="SKU" 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">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Status" /> #Html.DisplayNameFor(model => model.Status)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsMenuItem" /> #Html.DisplayNameFor(model => model.IsMenuItem)
</label>
</div>
<div class="form-group">
<label asp-for="Count" class="control-label"></label>
<input asp-for="Count" class="form-control" />
<span asp-validation-for="Count" class="text-danger"></span>
</div>
<div class="form-group">
<label>Categories :</label>
<select name="Id" asp-for="Id" class="formcontrol" asp-items="ViewBag.Categories">
<option value="#ViewBag.Categories"></option>
</select>
<span asp-validation-for="Id" 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");}
}
This is Product Model
public class Product
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string SKU { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public bool Status { get; set; }
public bool IsMenuItem { get; set; }
public int Count { get; set; }
public int? CategoryId { get; set; }
public Category Category { get; set; }
}
This is Category Model
public class Category
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
This is the result
I want to see Categories in dropdown list, how can I do it ?
I think that we must change attributes in <select> and <option> tags,
Please, help me to fix it
You are using the wrong action method. For create operation your controller should have two action methods - a GET method and a POST method. The one you are using now (marked with [HttpPost]) is for receiving data from your view, once you try to save them. To pass data to the view (while you are creating it), use an action method marked with [HttpGet] attribute (or, not marked with any attribute at all). Following GET method should be sufficient to create your view -
[HttpGet] // this attribute is optional
public IActionResult Create()
{
ViewBag.Categories = new SelectList(await _categoryrepo.GetAllAsync() , "Id" , "Name");
return View();
}
You are marking the select tag wrong, it should represent the foreign key property CategoryId, not the Id of the product -
<select asp-for="CategoryId" class ="form-control" asp-items="ViewBag.Categories"></select>
You don't need the option tag unless you want a display-value like Please select a Category or something -
<select asp-for="CategoryId" class ="form-control" asp-items="ViewBag.Categories">
<option value="">Please choose user category:</option>
</select>
EDIT :
I'm not sure what version of ASP.NET MVC or, ASP.NET Core MVC you are using, but the razor syntax above works in .NET 5.
you should have 2 actions -to create or edit view and save data.
Your action to create view should be looking like this:
[HttpGet] // this attribute is optional
public async Task<IActionResult> Create()
{
return await Edit(0);
}
[HttpGet("{id?}")]
public async Task<IActionResult> Edit(int id)
{
ProductModel model=null;
if id=0 model=new Product();
else model= await _productRepo.GetAsync(id);
ViewBag.Categories = new SelectList(await _categoryrepo.GetAllAsync()
, "Id" , "Name");
return View(model);
}
and fix your select:
<select name="categoryId" asp-for="CategoryId" class="formcontrol" asp-items="#ViewBag.Categories">
</select>
First of all forgive me as I am new to MVC.
I have a view which shows server(s) related to a particular application. If none exist, or the user wants to add an additional application/server relationship, they click on "Create New". From this link, I would like the ApplicationID to be passed to the Create view, and the selectlist for Applications to default to the correct Application. Or alternatively, display the application name/id in a label. I would also like to pass the ApplicationId back when "Back to List" is selected.
Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace ApplicationPortfolio.Models
{
[Table("tblServerApp")]
public class ServerApp
{
public int ServerAppId { get; set; }
public int? ApplicationId { get; set; }
public int? ServerId { get; set; }
public virtual Application Application { get; set; }
public virtual Server Server { get; set; }
}
}
Controller:
// GET: ServerApps/Create
public IActionResult Create(int? id)
{
ViewData["ApplicationId"] = new SelectList(_context.Application, "ApplicationID", "Name", id);
ViewData["ServerId"] = new SelectList(_context.Server, "ServerId", "ServerName");
return View();
}
View:
#model ApplicationPortfolio.Models.ServerApp
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Create</h1>
<h4>ServerApp</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">
<div class="form-group">
<label asp-for="ApplicationId" class="control-label"></label>
<select asp-for="ApplicationId" class="form-control" asp-items="ViewBag.ApplicationId"></select>
</div>
</div>
<div class="form-group">
<label asp-for="ServerId" class="control-label"></label>
<select asp-for="ServerId" class ="form-control" asp-items="ViewBag.ServerId"></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");}
}
After my test,I found your problem may in your code:
ViewData["ApplicationId"] = new SelectList(_context.Application, "ApplicationID", "Name", id);
ApplicationID and Name should be same with the name in your Application model.
If your model is(example) :
public class Application
{
[Key]
public int ApplicationId { get; set; }
public string ApplicationName { get; set; }
public List<ServerApp> ServerApps { get; set; }
}
Your code should be(Please pay attention to whether the case and match) :
ViewData["ApplicationId"] = new SelectList(_context.Applications, "ApplicationId", "ApplicationName",id);
And if you want to pass the ApplicationId back to Index,you can try following code.
#model ServerApp
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h4>ServerApp</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">
<div class="form-group">
<label asp-for="ApplicationId" class="control-label"></label>
<select id="select" asp-for="ApplicationId" class="form-control" asp-items="ViewBag.ApplicationId"></select>
</div>
</div>
<div class="form-group">
<label asp-for="ServerId" class="control-label"></label>
<select asp-for="ServerId" class="form-control" asp-items="ViewBag.ServerId"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<button id="submit" class="btn btn-info">Back to List</button>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$("#submit").click(function (e) {
e.preventDefault();
var e = document.getElementById("select");
var strUser = e.value;
window.location.href = "/home/index?id=" + strUser;
})
</script>
}
Test Result:
I am using MVC in a .NET Core 2 web app. I have an entity Schedule that contains meeting schedules between Counselor and Student (both are entities). In my Edit ViewModel for my Schedule I have a property for Counselor and Student - I use this for display only - to display their names and other info on the Schedule I am editing. I am using CounselorId and StudentId in my Schedule object itself. On my HttpGet Action for Edit I am populating my ViewModel just fine with the Counselor and Student. However, on the HttpPost Action of Edit I pass in my ViewModel and I see that my Counselor and Student are both NULL. I then get an error when trying to update my Schedule object. Here is the message:
The association between entity types 'Student' and 'Schedule' has been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should be deleted, then setup the relationship to use cascade deletes.
I found that if I hit the database to populate my model.Counselor and model.Student in my HttpPost Edit Action before I call update that it works. I want to know what the real fix is here using the best practice. Thanks for your help.
Here is my View:
#using CounselorSchedule.ViewModels
#model ScheduleEditViewModel
#{
ViewData["Title"] = "Edit Schedule";
}
<link href="~/css/bootstrap-datepicker.css" rel="stylesheet" />
#section Scripts{
<script src="~/scripts/boostrap-datepicker.min.js"></script>
<script>
$(document).ready(function () {
$(".datepicker").datepicker({ format: 'mm/dd/yyyy', autoclose: true, todayBtn: 'linked' })
});
</script>
}
<div class="row top-buffer">
<div class="col-sm-12">
<h2>Edit Schedule</h2>
</div>
</div>
<form asp-action="Edit">
<div class="row top-buffer">
<div class="col-sm-12">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
<div class="col-sm-4">
<h4>Schedule</h4>
</div>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="form-group">
<div class="col-sm-6">
<div class="editor-label">
<input type="hidden" asp-for="ScheduleId" />
<input type="hidden" asp-for="CounselorId" />
<input type="hidden" asp-for="StudentId" />
<label asp-for="Counselor" class="control-label"></label>
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Counselor.CounselorFirstName) #Html.DisplayFor(model => model.Counselor.CounselorLastName)
</div>
</div>
<div class="col-sm-6">
<div class="editor-label">
<label asp-for="Student" class="control-label"></label>
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Student.StudentFirstName) #Html.DisplayFor(model => model.Student.StudentLastName)
</div>
</div>
</div>
</div>
<div class="divide20"></div>
<div class="row">
<div class="form-group">
<div class="col-sm-4">
<div class="editor-label">
<label asp-for="ScheduleDate" class="control-label"></label>
</div>
<div class="editor-field">
<input asp-for="ScheduleDate" class="form-control datepicker" type="text" />
<span asp-validation-for="ScheduleDate" class="text-danger"></span>
</div>
</div>
<div class="col-sm-4">
<div class="editor-label">
<label asp-for="ScheduleTimeId" class="control-label"></label>
</div>
<div class="editor-field">
<select asp-for="ScheduleTimeId" class="form-control"
asp-items="#Model.ScheduleTimeList">
<option value="-1" selected>-</option>
</select>
<span asp-validation-for="ScheduleTimeId" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
<div class="panel-footer">
<div class="row">
<div class="col-sm-offset-6 col-sm-3">
<a class="btn btn-default btn-block"
asp-action="Details" asp-route-id="#Model.ScheduleId">Cancel</a>
</div>
<div class="col-sm-3">
<input type="submit" value="Update Case" class="btn btn-primary btn-block" />
</div>
</div>
</div>
</div>
</div>
</div>
</form>
Here is my controller code:
[HttpPost]
public IActionResult Edit(ScheduleEditViewModel model)
{
var schedule = _repository.GetScheduleDetails(model.ScheduleId);
if (ModelState.IsValid)
{
#region Allow reset of SelectList to "-"
//If "-" was selected then set to NULL in database
var scheduleTimeId = Convert.ToInt32(model.ScheduleTimeId);
if (scheduleTimeId == -1)
model.ScheduleTimeId = null;
#endregion
Mapper.Map(model, schedule);
_repository.PutSchedule(schedule);
return RedirectToAction("Details", "Schedule", new { #id = schedule.ScheduleId });
}
else
{
model = PopulateScheduleEditViewModel(model, schedule);
return View(model);
}
}
Here is my Edit View Model:
public class ScheduleEditViewModel
{
public int ScheduleId { get; set; }
public int CounselorId { get; set; }
public int StudentId { get; set; }
[Display(Name = "Counselor")]
public Counselor Counselor { get; set; }
[Display(Name = "Student")]
public Student Student { get; set; }
[Display(Name = "Schedule Date")]
[DataType(DataType.Date)]
public DateTime? ScheduleDate { get; set; }
[Display(Name = "Schedule Time")]
public int? ScheduleTimeId { get; set; }
public SelectList ScheduleTimeList { get; set; }
}