Home > Net >  Why select asp-for has no value in post handler
Why select asp-for has no value in post handler

Time:01-09

I have this form on my page. It's for adding some project to DB. Project requires director for itself, so i pass SelectList of workers to this form.

<form asp-controller="Project" asp-action="Edit" method="post">
...some other fields...
@if (ViewBag.workers != null)
    {
        <div>
            <label asp-for="director">Director</label>
            <select asp-for="director" [email protected]>
            </select>
            <span asp-validation-for="director"></span>
        </div>
    }
...
</form>

Select tag working, there are my workers. But when i'm trying to submit, i have error "The director field is required."

form with error

I've checked form with js and it has data from select option, but my post handler don't. handler argument

There is code of hadler

[HttpPost]
    public IActionResult Edit(Project model)
    {
        // here model hasn't director field
        if (ModelState.IsValid)
        {
            dataManager.project.SaveProject(model);
            return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Replace("Controller", ""));
        }
        ViewBag.workers = new SelectList(dataManager.worker.GetAllWorkers(), nameof(Worker.id), nameof(Worker.name));
        return View(model);
    }

And Project class code

public class Project
{
    [Required] public Guid id { set; get; }
    
    [Required] public string name { set; get; }
    
    public string customer { set; get; }

    public string executor { set; get; }

    public Worker director { set; get; }

    public DateTime start { set; get; }

    public DateTime end { set; get; }

    public uint priority { set; get; }
}

CodePudding user response:

Post the solution mentioned in the comment as the answer post as the reference for the future reader.

Issue & Concern

From this line:

ViewBag.workers = new SelectList(dataManager.worker.GetAllWorkers(), nameof(Worker.id), nameof(Worker.name));

You are setting the value of the drop-down list option as id. Unsure what is the id type, (possibly it is an int, Guid type) but I am sure that it is not a Worker (object) type.

While in the Project model, you specify director as Worker type.

public class Project
{
    ...

    public Worker director { set; get; }
}

Since an int/Guid type is unmatched with the Worker type, the API action is unable to bind the value that you passed from the View to the model. Thus, you will get Project as null in the API action.


Solution

  1. Would suggest that modify the property type in Project model as int/Guid (depending on your Worker ID type in the database) rather than using the Worker (object) type. And remove the Worker property as it is no longer needed and avoid the error in ModelState due to the value is not provided.

Note: If this Project model is the entity model that was generated (scaffolded) by Entity Framework, would suggest creating another class that acts as the DTO (Data Transfer Object). Then map the received DTO to entity model.

public class Project
{
    ...

    public Worker directorId { set; get; }
}
  1. In the View, bind the drop-down list with asp-for="workerId" to pass the selected value as workerId.
<select asp-for="directorId" [email protected]></select>
  1. Back-end side

    3.1. Key point: Query the Worker object with the received project.workerId. Then bind the Worker object to the entity model before inserting.

    3.2. (If implementing the DTO as mentioned in 1) You need to implement the logic (or look for mapping library such as AutoMapper) to map from DTO to the entity model.

  • Related