<select> multiselect i bindowanie modelu

Odpowiedz Nowy wątek
2018-07-15 16:39
0

Hej, nie umiem do końca skumać ja się binduje przy wielokrotnym wyborze w MVC6 lub Core2. Przykład zaczerpnięty z samouczka .net:

Posiadam model:

public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Display(Name = "Number")]
        public int CourseID { get; set; }

        [StringLength(50, MinimumLength = 3)]
        public string Title { get; set; }

        [Range(0, 5)]
        public int Credits { get; set; }

        public int DepartmentID { get; set; }

        public Department Department { get; set; }
        public ICollection<Enrollment> Enrollments { get; set; }
        public ICollection<CourseAssignment> CourseAssignments { get; set; }
    }

Kontroler dla edycji rekordu:

(...)
[HttpPost, ActionName("Edit")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> EditPost(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var courseToUpdate = await _context.Courses
                .SingleOrDefaultAsync(c => c.CourseID == id);

            if (await TryUpdateModelAsync<Course>(courseToUpdate,
                "",
                c => c.Credits, c => c.DepartmentID, c => c.Title))
            {
                try
                {
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateException /* ex */)
                {
                    //Log the error (uncomment ex variable name and write a log.)
                    ModelState.AddModelError("", "Unable to save changes. " +
                        "Try again, and if the problem persists, " +
                        "see your system administrator.");
                }
                return RedirectToAction(nameof(Index));
            }
            PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
            return View(courseToUpdate);
        }
(...)

Zapełnianie listy dropdown w kontrolerze:

(...)
private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                                   orderby d.Name
                                   select d;
            ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
        }
(...)

Oraz widok:

@model ContosoUniversity.Models.Course
(...)
<div class="form-group">
                <label asp-for="Department" class="control-label"></label>
                <select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
                    <option value="">-- Select Department --</option>
                </select>
                <span asp-validation-for="DepartmentID" class="text-danger" />
            </div>
(...)

I tu jest wszystko jasne dla mnie. W kontrolerze jest tworzona dynamiczna lista ViewBag, pobiera ona z kontekstu nazwy działów. Widok edycji pozwala wybrać jeden z działów poprzez <select>. Kontroler w akcji Edit, po wypełnieniu formularza nadpisuje dane. I to wszystko jest zrozumiałe.

Do końca nie wiem jak powinna wyglądać architektura, jeśli chcę dokonać wielokrotnego wyboru. DepartmentID, jako model w bazie EF nie może być tablicą. W takim razie powinien powstać ViewModel, np. typu IEnumerable<??> Departments {get;set;}, który zastąpi:

asp-for="DepartmentID"

na

asp-for="Departments"

Czy w takim wypadku ViewModel musi zawierać wszystkie pozostałe własności modelu? Bo rozumiem, że w widoku będziemy musieli wtedy wywołać @model ViewModel zamiast @model Model

Jeśli tak, to dalej nie wiem jak bindować wielokrotny wybór z bazą, by zapisać w niej wybór dla kilku DepartmentID. Na SO nie udało mi się znaleźć przykładu który by wyjaśnił mi zagadnienie bindowania VM z bazą/modelem.

edytowany 2x, ostatnio: bakunet, 2018-07-15 16:44

Pozostało 580 znaków

2018-07-15 22:36

Generalnie do widoku zawsze powinien iść viewmodel (nie model) Ale w razorze operujesz @Model. I teraz jeśli twój select to jest pojedynczy wybór Departamentu to w viewmodelu masz pole int DepartmentId. Jeśli masz wybór wielokrotny to w viewmodelu powinieneś mieć tablice tych Idków. Czyli int[] DepartmentsId. I to się powinno samo bindować. A jeśli już robisz .net core to czemu nie korzystasz np z wbudowanego kontenera ioc? Na twoim miejscu robiłbym w ten sposób: tworzę klasę np CourseService implementującą ICourseService z metodą CourseViewModel GetCourseById(int id) gdzie CourseViewModel {
public int Id {get;set;}
oraz pola które należy edytować w kursie
public int[] Departments // tu będą id departamentó które zaznaczysz na selekcje lub pobierzesz z bazy
public ICollection<departmentsviewmodel> Departments //lista departmentów do selecta
}
ICourseService wstrzykujesz w konstruktor controllera

edytowany 3x, ostatnio: szydlak, 2018-07-16 00:06
Dzięki. Rozumiem że po wysłaniu formularza, HttpPost powinien przejąć kolekcję wybranych działów, tak? Doszedłem też do tego, że ViewBag powinien wołać instancję new SelectListItem. Czy w ViewModel własność nazw powinna przyjąć typ IEnumerable<selectlistitem> Names {get; set;} ? - bakunet 2018-07-15 23:19
Dla mnie ta akcja Edit jest bez sensu. Bo coś tam niby aktualizujesz do bazy, tylko skąd jeśli w parametrach nie ma żadnego modelu ? - szydlak 2018-07-15 23:34
W Edit masz c => c.Credits, c => c.DepartmentID, c => c.Title)), przekazywane z formy widoku - bakunet 2018-07-16 06:36
TryUpdateModelAsync to jest wbudowane w core ?, To w ogóle działa? pierwszy raz takie cuda widzę. Znalazłem w dokumentacji. Wygląda, że tak to działa. Ale jak mówie ja na twoim miejscu robiłbym to viewmodelem - szydlak 2018-07-16 08:16
To jest przykład z dokumentacji, który próbuję przerobić na multiselect :) c. to jest model Course. W każdym razie utworzyłem sobie prostszy przykład i na nim próbuję różne rozwiązania. - bakunet 2018-07-16 09:39

Pozostało 580 znaków

2018-07-16 22:44
0
szydlak napisał(a):

Generalnie do widoku zawsze powinien iść viewmodel (nie model) Ale w razorze operujesz @Model. I teraz jeśli twój select to jest pojedynczy wybór Departamentu to w viewmodelu masz pole int DepartmentId. Jeśli masz wybór wielokrotny to w viewmodelu powinieneś mieć tablice tych Idków. Czyli int[] DepartmentsId. I to się powinno samo bindować. A jeśli już robisz .net core to czemu nie korzystasz np z wbudowanego kontenera ioc? Na twoim miejscu robiłbym w ten sposób: tworzę klasę np CourseService implementującą ICourseService z metodą CourseViewModel GetCourseById(int id) gdzie CourseViewModel {
public int Id {get;set;}
oraz pola które należy edytować w kursie
public int[] Departments // tu będą id departamentó które zaznaczysz na selekcje lub pobierzesz z bazy
public ICollection<departmentsviewmodel> Departments //lista departmentów do selecta
}
ICourseService wstrzykujesz w konstruktor controllera

Poszedłem za Twoją radą, też większość przykładów w necie prawiło o ViewModelach. Więc nauczyłem się tworzyć i wykorzystywać VM w ASP.NET. Robi się to nieco inaczej niż w WPF MVVM. Do tego właśnie udało mi się stworzyć działający program który też przekazuje info z formularza widoku z dropdownem przez kontroler do kontekstu bazy. Trzy dni prób i błędów uważam za owocne. O kontenerze IOC dowiedziałem się dopiero od Ciebie. Trochę ku mojemu zaskoczeniu, bo jestem świeżo po przerobieniu "asp-net-core-mvc-2-zaawansowane-programowanie" i nie było w książce słowa o tym. Też w żadnym tutku nie spotkałem się z czymś takim. Z tego co piszesz to wnioskuję, że wygodnie się z tym pracuje? Jak do końca ogarnę swoją aplikację testową, to przysiądę nad IOC. Bądź co bądź, dzięki za wskazówki.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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