POST - model "gubi" dane

0

Cześć, w metodzie GET przekazuję do widoku "pusty model" dni tygodnia (zmiany w pracy danego pracownika). Niestety przy wysłaniu tych danych, do akcji kontrolera model jakby zgubił część z nich, dane pracownika (imię, nazwisko itp.) są przekazane poprawnie, natomiast lista zmian już idzie z nullem, czy mam coś błędnie zmapowane? Jak wystrzec się takich błędów?

Lista<shift> z widoku:

                    <table class="table">
                        <caption>Grafik pracownika</caption>
                        <thead>
                            <tr>
                                <th scope="col">Dzień tygodnia</th>
                                <th scope="col">Od godziny</th>
                                <th scope="col">Do godziny</th>
                                <th scope="col">Wolne</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var shift in Model.Shifts)
                            {
                            <tr>
                                <td><label asp-for="@shift.DayOfWeek" />@shift.DayOfWeek</td>
                                <td><input id="@(shift.DayOfWeek + "dateFrom")" class="form-control" asp-for="@shift.DateFrom" type=time /></td>
                                <td><input id="@(shift.DayOfWeek+ "dateTo")" class="form-control" asp-for="@shift.DateTo" type=time /></td>
                                <td><input type="checkbox" class="form-check-input text-center" data-day="@shift.DayOfWeek" id="@(shift.DayOfWeek+"freeDayCheckbox")" onclick="changeEditable(this.getAttribute('data-day'))"></td>
                            </tr>
                            }
                        </tbody>
                    </table>

Akcja POST kontrolera:

        [HttpPost]
        public IActionResult AddWorker(WorkerModel model)
        {
            var workerModel = _workerService.AddWorker(model);
            return RedirectToAction("EditWorkplace", "Workplace", new { Id = workerModel.WorkplaceId });
        }

Model Pracownika

    public class WorkerModel
    {
        public int Id { get; set; }
        [Required]
        [DisplayName("Imię")]
        public string Name { get; set; }
        [DisplayName("Nazwisko")]
        public string LastName { get; set; }
        [Display(Name = "Zdjęcie")]
        public IFormFile Photo { get; set; }
        public string PhotoFilePath { get; set; }
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int WorkplaceId { get; set; }
        public List<ServiceModel> Services { get; set; }
        public List<ShiftModel> Shifts { get; set; }
    }

Model Zmiany

    public class ShiftModel
    {
        public int Id { get; set; }
        [DataType(DataType.Time)]
        [DisplayFormat(DataFormatString = "{0:hh:mm}", ApplyFormatInEditMode = true)]
        public DateTime DateFrom { get; set; }
        [DataType(DataType.Time)]
        [DisplayFormat(DataFormatString = "{0:hh:mm}", ApplyFormatInEditMode = true)]
        public DateTime DateTo { get; set; }
        public string DayOfWeek { get; set; }
        public int WorkerId { get; set; }
        public bool DayOff { get; set; }
    }
0

Raczej będziesz musiał podać cały kod widoku z którego robisz POSTa, jak i kod akcji do pobrania danych i wyświetlenia widoku, żeby ktoś mógł dostrzec co tam nawywijałeś.
Poza tym zawsze upewniaj się w narzędziach developerskich przeglądarki, czy dane faktycznie nie zostały wysłane, bo jeśli zostały, to pewnie trzeba dać znać frameworkowi jawnie skąd ma sobie bindować model.

0
urke napisał(a):

Raczej będziesz musiał podać cały kod widoku z którego robisz POSTa, jak i kod akcji do pobrania danych i wyświetlenia widoku, żeby ktoś mógł dostrzec co tam nawywijałeś.

Widok:

@model Application.Models.WorkerModel
@{
    ViewBag.Title = "Dodawanie pracownika";
    Layout = "~/Views/Shared/_Layout.cshtml";
    var photoPath = "~/img/" + (Model.PhotoFilePath ?? "noimage.jpg");
}

    <h1>Dodawanie pracownika</h1>
    <form enctype="multipart/form-data" method="post" class="mt-3">
        <div class="container">
            <div class="row">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="col">
                    <input type="hidden" asp-for="WorkplaceId" />
                    <div class="form-group">
                        <label asp-for="Name" class="col-form-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="LastName" class="col-form-label"></label>
                        <input asp-for="LastName" class="form-control" />
                        <span asp-validation-for="LastName" class="text-danger"></span>
                    </div>
                    <div class="form-group">
                        <label asp-for="Photo" class="col-form-label"></label>
                        <div class="custom-file">
                            <input asp-for="Photo" class="form-control custom-file-input" />
                            @if (photoPath == "~/img/noimage.jpg")
                            {
                                <label class="custom-file-label">Wybierz plik...</label>
                            }
                            else
                            {
                                <label class="custom-file-label">Kliknij aby zmienić zdjęcie</label>
                            }
                        </div>
                        <span asp-validation-for="Photo" class="text-danger"></span>
                    </div>
                    <table class="table">
                        <caption>Grafik pracownika</caption>
                        <thead>
                            <tr>
                                <th scope="col">Dzień tygodnia</th>
                                <th scope="col">Od godziny</th>
                                <th scope="col">Do godziny</th>
                                <th scope="col">Wolne</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var shift in Model.Shifts)
                            {
                            <tr>
                                <td><label asp-for="@shift.DayOfWeek" />@shift.DayOfWeek</td>
                                <td><input id="@(shift.DayOfWeek + "dateFrom")" class="form-control" asp-for="@shift.DateFrom" type=time /></td>
                                <td><input id="@(shift.DayOfWeek+ "dateTo")" class="form-control" asp-for="@shift.DateTo" type=time /></td>
                                <td><input type="checkbox" class="form-check-input text-center" data-day="@shift.DayOfWeek" id="@(shift.DayOfWeek+"freeDayCheckbox")" onclick="changeEditable(this.getAttribute('data-day'))"></td>
                            </tr>
                            }
                        </tbody>
                    </table>
                    <div class="form-group row">
                        <div class="col">
                            <button type="submit" class="btn btn-primary form-control">Dodaj pracownika</button>
                        </div>
                        <div class="col">
                            <a asp-action="Edit" asp-controller="Workplace" asp-route-Id="@Model.WorkplaceId" class="btn btn-danger">Powrót do salonu</a>
                        </div>
                    </div>

                </div>
                <div class="col">
                    <img class="card-img-top" width="250" height="250" src="@photoPath" asp-append-version="true" />
                    <div class="card">
                        <div class="card-header">
                            <h3>Lista usług</h3>
                        </div>
                        <div class="card-body">
                            <h5 class="card-title">Brak dodanych usług</h5>
                        </div>
                        <div class="card-footer">
                            <a class="btn btn-primary">Dodaj usługę</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>
    @section Scripts {
        <script>

            function changeEditable(dayOfWeek) {
                var checkbox = document.getElementById(dayOfWeek + "freeDayCheckbox");
                if (checkbox.checked == true) {
                    document.getElementById(dayOfWeek + "dateTo").readOnly = true
                    document.getElementById(dayOfWeek + "dateFrom").readOnly = true
                }
                else
                    {
                    document.getElementById(dayOfWeek + "dateTo").readOnly = false
                    document.getElementById(dayOfWeek + "dateFrom").readOnly = false
                };
                };
        </script>
    }

Akcja kontrolera wyświetlająca widok:

        [HttpGet]
        public IActionResult AddWorker(int WorkplaceId)
        {
            List<ShiftModel> shifts = new List<ShiftModel>()
            {
                new ShiftModel()
                {
                    DayOfWeek = "Poniedziałek",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Wtorek",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Środa",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Czwartek",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Piątek",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Sobota",
                },
                new ShiftModel()
                {
                    DayOfWeek = "Niedziela",
                }
            };

            var model = new WorkerModel()
            {
                WorkplaceId = WorkplaceId,
                Shifts = shifts
            };
            return View(model);
        }

Poza tym zawsze upewniaj się w narzędziach developerskich przeglądarki, czy dane faktycznie nie zostały wysłane, bo jeśli zostały, to pewnie trzeba dać znać frameworkowi jawnie skąd ma sobie bindować model.

Jak to sprawdzić, tzn w którym miejscu mogę to wyłapać?

1

Postujesz listę obiektów, więc musisz postować z indeksem w nazwie. Inaczej będziesz miał wiele pól o tej samej nazwie w POST.
Paczaj to: https://www.codeproject.com/Questions/5273545/Pass-list-of-objects-from-view-to-controller

EDIT: @Krispekowy zły link Ci podesłałem. Miałem na myśli to: https://stackoverflow.com/a/60887179
Tam jest ładnie pokazane skąd się bierze items[i].Name (index) w bindingu.

A tutaj jeszcze lepiej wyjaśnione: https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections

Niestety MS zrobił sporo zamieszania z nowymi wersjami frameworku i Razora, dlatego warto przy googlani użyć Tools -> Any Time -> Past Year w Google...

0

@0xmarcin: A czy muszę robić to przez AJAX?

0

Możesz też dodać swój model binder, jednak najlepszym rozwiązaniem wydaje mi się nazwanie pól w sposób zgodny z regułami już istniejącego, wbudowanego w mvc bindera.

0

A nie potrzebujesz czasem atrybutu FromBody ?

1
List<ShiftModel> shifts = new List<ShiftModel>()
            {
                new ShiftModel()
                {
                    DayOfWeek = "Poniedziałek",
                },
// ...
                new ShiftModel()
                {
                    DayOfWeek = "Niedziela",
                }
            };

=>

private readonly static List<ShiftModel> shifts = DateTimeFormatInfo.CurrentInfo.DayNames.Select(dn => new ShiftModel() { DayOfWeek = char.ToUpper(dn[0]) + dn.Substring(1) }).ToList();

1 użytkowników online, w tym zalogowanych: 0, gości: 1