Как привязать динамические сложные объекты, созданные с использованием частичного просмотра, к свойству коллекции в view-model

#asp.net-core #model-view-controller #c#-4.0 #asp.net-core-mvc #partial-views

#asp.net-core #model-view-controller #c #-4.0 #asp.net-core-mvc #частичные представления

Вопрос:

Я не могу привязать коллекцию дочерних объектов, созданных динамически с использованием свойства частичного представления к view-model IEnumerable .

Я успешно привязал объекты, созданные динамически с использованием частичных представлений, к view-model, используя метод, который я нашел в этом блогеhttps://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx /. Я следовал той же технике, но я не могу привязать коллекцию к IEnumerable свойству в view-model.

 [BindRequired]
public class EmployeeViewModel
{
   other properties....
   public IEnumerable<ContactDetailViewModel> EmployeeContact { get; set; }
}

[BindRequired]
public class ContactDetailViewModel
{
   // I use this as my indexer for dynamic elements
   public string RecordId { get; set; } = Guid.NewGuid().ToString();

   public string Telephone { get; set; }

   public string EmailAddress { get; set; }

   public string ContactDescription { get; set; }
}
  

Я вызываю этот метод действия через ajax для добавления динамических элементов контактной информации, и он возвращает частичный просмотр в виде html, и он отлично работает.

 [Route("[action]", Name = "BlankEmployeeContactDetail"), HttpGet("AddBlankContactDetail")]
public PartialViewResult AddBlankContactDetail()
{
            return PartialView("_ContactInformation", new     ContactDetailViewModel());
}
  

Первоначальная контактная информация добавляется в основной вид с помощью следующего, пожалуйста, перейдите по этой ссылкеhttps://1drv.ms/u/s !AkRSHVUtFlKhuHaxH96Ik4ineATE для загрузки файлов основного вида и частичного просмотра cshtml. Также следует отметить, что привязка модели не выполняется для всех других свойств, когда я включаю этот частичный просмотр, но работает, когда я его комментирую. Я сбит с толку и был бы очень признателен за любую помощь, которую вы можете мне предоставить.

 <section id="widget-grid" class="">
   <div class="row contactContainer">
     @{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel()); }
   </div>
</section>
  

Это метод действия контроллера, к которому я пытаюсь привязать опубликованные данные:

 [Route("[action]"), HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
        public IActionResult Register([FromForm] EmployeeViewModel model, [FromQuery] string returnUrl = null)
{
    if (ModelState.IsValid)
    {

    }

    return View(model);
}
  

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

1. Я не понимаю, где вы моделируете привязку чего-либо здесь. Ваше действие не имеет параметров, и вы создаете экземпляр своей модели представления напрямую.

2. Добрый день, спасибо за комментарий, я прикрепил контроллер по ссылке выше, но я отредактирую, чтобы включить метод action, который получает опубликованные данные.

Ответ №1:

Для привязки входные имена в значительной степени соответствуют определенному соглашению, которое соответствует тому, к чему вы привязываетесь. Хотя из вашего вопроса неясно, я предполагаю, что вы пытаетесь в конечном итоге привязаться к экземпляру EmployeeViewModel , что означает, что для ввода вашей контактной информации потребуются имена типа: EmployeeContact[0].Telephone , но когда вы передаете экземпляр ContactDetailViewModel в качестве «модели» частичного просмотра, имена будут просто Telephone , и, что еще хуже, эти одни и те же имена будут повторяться снова и снова, т. Е. каждый созданный вами набор полей контактной информации будет иметь вход с именем просто Telephone .

Короче говоря, вам нужен контекст всей модели для генерации правильных входных имен. У вас есть несколько вариантов.

Поскольку вы извлекаете набор полей с помощью запроса AJAX, можно было бы передать «префикс» для использования вместе с этим запросом. Другими словами, вы можете отслеживать значение индекса, подсчитывая, сколько из этих разделов вы добавили, а затем отправлять вместе с запросом на новый раздел что-то вроде

 prefix: 'EmployeeContact['   (i   1)   ']',
  

Затем в вашем частичном представлении:

 @{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel(), new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = ViewBag.Prefix } } ); }
  

Это немного халтурно и, честно говоря, вероятно, довольно подвержено ошибкам. Лучшим вариантом было бы использовать совершенно другой подход. Вместо обратного вызова для получения частичного представления определите его только один раз в качестве шаблона:

 <script type="text/html" id="ContactInformationTemplate">
    <!-- HTML for contact information inputs -->
</script>
  

Затем, используя библиотеку, такую как Vue, React, Angular и т.д., Вы можете настроить конструкцию «foreach», привязанную к определенному массиву JavaScript, который использует этот шаблон для отображения элементов в этом массиве. Затем добавление нового набора входных данных так же просто, как добавление нового элемента в массив. Вам придется выполнить некоторые работы, чтобы настроить входные имена на основе индекса элемента в массиве, но у всех этих клиентских фреймворков есть способы сделать это. Это также имело бы побочное преимущество в том, что не нужно было бы отправлять AJAX-запрос каждый раз, когда вы хотите добавить новый раздел.

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

1. Добрый день, спасибо за ваш любезный комментарий, я сделал это, и теперь он работает нормально. Из-за этого это не работало name="EmployeeContact[@(Model.RecordId)].ContactDescription" , я использовал name="model[@(Model.RecordId)].ContactDescription" , поэтому использование имени свойства помогает связующему модели понять, как связать какие элементы с какими свойствами. Я целый день боролся с этим, и у меня даже развилась мучительная головная боль, которую я сейчас лечу.

2. Я использую public string RecordId { get; set; } = Guid.NewGuid().ToString(); для создания индексатора, и он работает как шарм, и это объясняет эту строку кода new ContactDetailViewModel()