Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

Динамически формы с мультиэлементами в Razor pages

#c_sharp #aspnet_mvc #entity_framework #aspnet_core #razor


Есть простая база данных клиентов. В ней 2 таблицы, в одной из которых клиент с информацией
и 2 таблица с его почтовыми ящиками. Внешний ключ связывает эти таблицы по id клиента.
Есть такой код добавления на бекенде в базу данных:

public async Task Handle(CreateCommand request, CancellationToken cancellationToken)
{
    var entity = new Customer
    {
        CompanyName = request.CompanyName,
        Country = request.Country,
        City = request.City,
        Address = request.Address,
        ZipCode = request.ZipCode,
    };
    _context.Customers.Add(entity);

    await _context.SaveChangesAsync(cancellationToken);

    foreach (var createCustEmail in request.Emails)
    {
        _context.CustomerEmails.Add(new CustomerEmail()
        {
            CustomerId = entity.CustomerId,
            Email = createCustEmail.Email,
        });
    }
    await _context.SaveChangesAsync(cancellationToken);

    return Unit.Value;
}


Вопрос заключается в том,как сделать  добавление емейлов на стороне клиента в Razor
Pages?
    


Ответы

Ответ 1



У меня есть несколько разных солюшенов, которые отрабатывают разные аспекты мультиформ, вопрос реально достаточно объёмный, поэтому вам рекомендую сделать так же: прежде, чем делать реальное приложение сделать несколько тестовых, чтобы поиграться и посмотреть отдельные аспекты. Во-первых, вы можете начать с приложения в котором у вас модель емейла будет представлять простую строку: public class Customer { public int Id { get; set; } public string Title { get; set; } public string[] Phones { get; set; } } Это иногда очень удобно, в этом случае рекомендую посмотреть как хранить подобные структуры в базе используя Entity Framework. Мы же будем начинать с двух полноценных моделей (т.е. телефону нужно хранить не только сам номер, но и допустим тип - рабочий/домашний/факс) Модель контакта (мысленно можете заменять на Customer): public class Contact { public Contact() { this.Phones = new List(); } public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection Phones { get; set; } } Модель телефона (показаны только ключевые поля): public class Phone { public int Id { get; set; } public int ContactId { get; set; } public virtual Contact Contact { get; set; } public string Number { get; set; } } Далее тупо создаём CRUD-контроллер, используя scaffolding. Вы наверное многократно видели подобные заготовки, в них нет ничего сложного, но увы, они не умеют в мультиэлементы. Что нужно сделать. Во-первых, добавить в форму блок для отображения div'а с телефонными номерами:
@Html.EditorFor(x => x.Phones)

Добавить номер

Целиком файл формы Create: @model WebApplication1.Models.Contact @{ ViewBag.Title = "Create"; }

Create

@using (Html.BeginForm()) { @Html.AntiForgeryToken()

Contact


@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
@Html.EditorFor(x => x.Phones)
}
@Html.ActionLink("Back to List", "Index")
@section Scripts { @Scripts.Render("~/bundles/jqueryval") } Чтобы эта конструкция заработала - нужно добавить папку Views\Contacts\EditorTemplates и в ней расположить файл Phone.cshtml (который и будет определять как отобразить наш элемент редактирования: @model WebApplication1.Models.Phone
@Html.HiddenFor(x => x.Id) @Html.HiddenFor(x => x.ContactId)

@Html.TextBoxFor(x => x.Number)

Удалить
Итого, получается что-то типа: И выглядит как-то так (стилями css не украшал, так как проект учебный, поэтому выглядит страшненько): Почитайте букварь по этой теме, чтобы понимать, как это работает: Переопределение шаблонов отображения и редактирования В asp.net core принято создавать формы используя тэг-хелперы и аналога для Html.EditorFor я не знаю, но когда я смешал два стиля и прописал Html.EditorFor - оно сработало. Ну это просто некрасиво, слишком разнородный стиль. Но основная проблема связана с тем, что шаблоны DisplayTemplates/EditorTemplates для razor pages не работают, даже в 2.1 - по крайней мере когда я пробовал и в /Pages и в /Views/Shared разные варианты не сработал ни один, увы ;( Поэтому я и показывать стал на классическом asp'е, тем более что и проекты есть готовые ;) Какие подводные камни вас ещё ждут. Когда вы получаете данные с формы - всё у вас хорошо, но когда вы начинаете сохранять телефоны при редактировании - вам нужно определять какие из телефонов были удалены, какие добавлены и какие изменились -- и соответственно, удалённые - удалить из базы, новые добавить, а остальные изменить. Это отдельная большая тема, которая выпивает множество крови. Я вас отправлю к вот этому и этому вопросу, но будьте готовы потратить несколько дней на эту коварную тему, в этой ветке я просто обозначаю проблему. При помощи GraphDiff у вас получится сократить код до примерно такого: // POST: Contacts/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(Contact contact) { if (!this.ModelState.IsValid) return this.View(contact); var entity = db.Contacts.Include(x=>x.Phones).SingleOrDefault(x => x.Id == contact.Id); if (entity == null) return this.HttpNotFound(); this.db.UpdateGraph(contact, map => map.OwnedCollection(p => p.Phones)); this.db.SaveChanges(); return this.RedirectToAction("Index"); } А на ванильном c# код будет раза в четыре больше. Другой момент будет как смапить ваши классы на MediatR, это смотрите сами по конкретному проекту. И ещё один момент я упустил - javascript на кнопочках добавления/удаления записей. Я его в тестовом проекте не делал, да и он различается для ванильного js/jQuery/vue/etc - поэтому пишите свой по месту.

Комментариев нет:

Отправить комментарий