Общепринятый подход в MVC для формы в диалоге jQuery - PullRequest
18 голосов
/ 13 октября 2011

Похоже, существует несколько способов интеграции диалогов jQuery с ASP.NET MVC. Появился ли конкретный подход как общепринятый наилучший способ сделать это?

В качестве примера: у меня есть страница списка, где нажатие кнопки «изменить» для любого из перечисленных элементов открывает форму в диалоговом окне jQuery, заполненном деталями элемента. Пользователь редактирует данные и нажимает кнопку «Сохранить». Если сохранение выполнено успешно на стороне сервера, диалоговое окно закрывается и список перестраивается со свежими данными. Если при сбое сохранения на стороне сервера диалог остается открытым и для пользователя отображаются сообщения об ошибках.

  1. Подход без JSON: каждая ссылка «редактировать» является HREF для действия «редактировать» контроллера. Это действие контроллера создает представление, идентичное представлению «списка», плюс включает частичное действие для создания формы редактирования, ее заполнения и определения javascript для его открытия в виде диалога jquery. «Сохранить» - это форма-пост; в случае успеха он возвращает действие перенаправления обратно на страницу списка. Если происходит сбой, он перестраивает всю страницу (включая форму, которая появляется в диалоговом окне), отображая также сообщения об ошибках.
  2. Подход All-JSON: Страница списка отображает пустую форму редактирования (скрытую), готовую к открытию в диалоге. Ссылка «edit» вызывает локальный javascript, который выполняет ajax-запрос для получения полного объекта (я определяю контроллер, который возвращает полный объект как JsonResult). В случае успеха он заполняет форму редактирования данными объекта и открывает диалог. Ссылка «save» вызывает локальный javascript, который связывает данные формы в объект json, и вызывает операцию post с этим объектом json в качестве полезной нагрузки (я определяю контроллер, который ожидает этот объект, пытается сохранить и возвращает JsonResult, указывающий на успех / отказ + ErrorMessages). Обратный вызов при успешном выполнении запроса ajax оценивает возвращенный объект и либо отображает сообщения об ошибках в все еще открытом диалоговом окне jquery, либо закрывает диалоговое окно и перезагружает страницу «список» для получения свежих данных в списке.
  3. [Редактировать] Ajax-HTML подход: только что увидел это обсуждение SO , которое описывает другой подход. «Edit» вызывает локальный javascript, который делает ajax-пост, чтобы получить полный HTML-код диалога (я бы написал контроллер, который возвращает частичное представление: полностью заполненную форму). Он отображает возвращенный HTML-код в диалоге jquery, а также «переотправляет» отправку формы для создания ajax-записи содержимого формы (я бы написал контроллер httpPost, такой же, как в # 2 выше). Обратный вызов успеха оценивает ответ и заполняет сообщения об ошибках или закрывает диалог.
  4. Какой-нибудь другой крутой подход, о котором я не думал?

Вариант 1, кажется, больше соответствует «чистому» ASP.NET MVC. Однако, похоже, он имеет большие полезные нагрузки HTTP (поскольку мы отправляем полную страницу обратно в браузер при каждом запросе) и более медленную производительность на стороне сервера (поскольку мы перестраиваем список при каждом запросе).

Вариант 2 кажется более совместимым с более современными веб-приложениями на основе Ajax (меньшие полезные нагрузки HTTP, более детальные операции). Однако, похоже, что многие контроллеры будут контроллерами JSON, и я буду писать много кода на стороне клиента для маршалинга данных из объектов JSON в / из полей формы, отображения сообщений об ошибках и т. Д. не хватает многих интересных функций MVC, таких как EditorFor () и ValidationMessageFor (). Мне просто кажется, что я работаю с системой MVC, а не с ней.

[Редактировать: добавлена ​​опция 3] Вариант 3 кажется гибридом между 1 и 2. Я использую «чистый» подход MVC для построения и заполнения формы, и возвращаю полностью HTML-тег HTML. Возврат HTML в запрос ajax кажется странным, поскольку он настолько многословен, но я могу с этим справиться. Операция post - это хороший, компактный JSON, который "чувствует" лучше, чем ajax Однако, к сожалению, объект полезной нагрузки является FormCollection, а не реальным объектом viewmodel. Похоже, я могу использовать некоторые удобства MVC (EditorFor ()), но не другие (ValidationMessageFor ()).

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

Я довольно опытный в ASP.NET/C#, но я довольно новичок в MVC. Заранее спасибо за помощь!

[Редактировать] - выдающиеся ответы - хотелось бы присудить несколько ответов / наград, так как я нашел несколько ответов чрезвычайно полезными. Но так как я не могу, я отмечаю ответ с наибольшим количеством голосов как ответ. Еще раз спасибо всем респондентам!

Ответы [ 5 ]

12 голосов
/ 14 октября 2011

Моя команда и я обладаем большим опытом написания приложений MVC с поддержкой AJAX, и мы использовали все 3 подхода.

Однако, мой любимый, безусловно, AJAX-HTML подход -- Используйте PartialView для визуализации содержимого диалогового окна, которое может включать в себя сообщения проверки на стороне сервера и любую другую логику.

Самым большим преимуществом этого подхода является разделение интересов - ваши представления всегда отвечают за отображение вашего HTML, и ваш JavaScript не должен содержать какие-либотекст, разметка или «шаблоны», необходимые для визуализации JSON.

Еще одним большим преимуществом является то, что для рендеринга HTML доступны все замечательные функции MVC: строго типизированные представления, HtmlHelper, DisplayFor и EditorFor шаблоны, DataAnnotations и т. Д. Это делает еголегче быть последовательным и хорошо поддается рефакторингу.

Просто помните, что нет необходимости придерживаться единого подхода.Когда вашему AJAX-вызову нужно только что-то простое, например, обновление статуса, например «Успех», хорошо просто использовать string или JSON для передачи этих сообщений.Используйте PartialViews, когда требуется HTML, и используйте более простые методы, когда требуется связь.

4 голосов
/ 18 октября 2011

Ваш второй метод, подход All-JSON , кажется все более распространенным в клиентских библиотеках MVC и MVVM, таких как Knockout

В этом случае вы можете получить все данные в JSON (включая список) и редактировать элементы списка (аналогично их демонстрационному редактору элементов списка , просто с отображением диалога вместо встроенного и связыванием). данные, предназначенные только для чтения, охватывают ваши ячейки), а затем сериализуют весь набор данных обратно на сервер при сохранении. Или вы можете сделать это с частичным сохранением еды после каждого всплывающего редактирования.

JSFiddle: http://jsfiddle.net/paultyng/weLtH/17/

JS можно немного почистить, я не включил кнопку сохранения, но вы должны понять. Диалог редактирования может быть одним шаблоном, привязанным к одному редактированию, вместо того, чтобы делать это для каждой строки, это был просто самый простой способ сделать это с Knockout.

2 голосов
/ 21 октября 2011

Использование jQuery.tmpl() плагин 1

Я использую похожий сценарий, но делаю это по-другому.

У меня есть основной список, в который упакован каждый элементв контейнере (будь то таблица TR или DIV).Некоторая общая информация об элементе отображается в основном списке, когда в простом списке слишком много данных.Следовательно, основной список, а не детали (конечно).

Каждый контейнер элемента обычно пишется так:

<div class="item" data='<%= item.ToJson() %>'> <!-- single quotes! -->
    item visible list data
</div>

Основная часть здесь - это мой пользовательский метод расширения ToJson(), который сериализуетмои данные об элементе, которые можно легко использовать на клиенте.

Тогда у меня есть шаблон jQuery в конце списка, содержащегося внутри тега script.Этот шаблон является фактическим содержимым диалога редактирования со всеми переменными элемента, установленными по мере необходимости.

Всякий раз, когда пользователь нажимает редактировать на элементе, я просто анализирую элемент JSON по:

// in edit link click handler
var itemData = $.parseJSON($(this).closest("[data]").attr("data"));
// display dialog
displayDialog($("#editDialog").tmpl(itemData));

Мой диалог позаботится об остальном с помощью Ajax-вызовов и закроет диалоговое окно, если вызовы будут успешными или пользователь отменит редактирование.

Благодаря этому мой HTML-код будет минимальным (с одним шаблоном), а также JSONсодержит только те свойства, которые действительно необходимы.

Что делать, когда в вашем классе модели есть другие ссылки на сущности?

Это довольно часто, когда сущности связаны между собой.Предположим, у вас есть Post и Comment сущности:

public class Post
{
    public int Id { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }
}

Конечно, вы не заинтересованы в связанных сущностях при преобразовании ваших элементов в JSON на сервере.Вот почему вы можете поместить атрибут в связанные свойства, чтобы они не включались в JSON:

public class Post
{
    public int Id { get; set; }

    public string Body { get; set; }

    [ScriptIgnore]   
    public IList<Comment> Comments { get; set; }
}

Это заставит сериализатор JSON игнорировать связанное свойство, которое мы не будем редактировать в редакторе деталей.

ToJson() код расширения

Последнее, что нужно здесь указать, это код метода расширения, поэтому здесь все сказано:

public static class ObjectExtensions
{
    /// <summary>
    /// Serializes this object instance to JSON string.
    /// </summary>
    /// <param name="instance">Object instance being extended.</param>
    /// <returns>Returns a JSON string that represents current object instance.</returns>
    public static string ToJson(this object instance)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        // register custom class converters
        serializer.RegisterConverters(...);
        return serializer.Serialize(instance);
    }

    /// <summary>
    /// Serializes this object instance to JSON string.
    /// </summary>
    /// <param name="instance">Object instance being extended.</param>
    /// <param name="recursionDepth">Serialization recursion limit depth.</param>
    /// <returns>Returns a JSON string that represents current object instance.</returns>
    public static string ToJson(this object instance, int recursionDepth)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        // register custom class converters
        serializer.RegisterConverters(...);
        serializer.RecursionLimit = recursionDepth;
        return serializer.Serialize(instance);
    }
}

Что делать, когда объекты слишком сложные?

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

  • ваши объекты являются сложными или имеют длинные данные (длинные в терминах сериализованной строки)
  • определение объектов JSON для всех элементов будет слишком трудоемким
  • неизвестный объект JSON во время отображения основного списка

, тогда вы всегда можете отправить удаленный вызов Ajax на сервер для одного объекта и вернуть либо

  • объект JSON виспользовать с шаблоном диалога
  • вернуть частичное представление с редактором

Второй подход лучше, поскольку преобразование экземпляра объекта в HTML или JSON практически аналогичная задача.но во втором случае нет необходимости в обработке шаблонов на стороне клиента.

Но не используйте удаленные запросы только потому, что вы можете .Используйте подход, который проще и эффективнее для рассматриваемой проблемы.

1 : Справочник по шаблону jQuery

2 голосов
/ 21 октября 2011

Хорошо, так что ваши варианты в значительной степени поддерживают концепцию прогрессивного улучшения здесь.2 и 3 не будут работать, если ваш клиент не поддерживает java-скрипт.Очевидно, что все в порядке, если вам все равно, но я думаю, что я постараюсь спроектировать вещи так, чтобы они изящно деградировали, а вы просите о наилучшей практике здесь.

Итак, способ, которым я бы это построил, начинается сваш вариант 1. У вас есть кнопки редактирования, которые запускают другое действие, которое загружает страницу редактирования, и эта страница разработана со всеми валидаторами и т. д. в соответствии с обычным mvc.Это ваша базовая функциональность, поэтому она работает без js.

Итак, следующий вопрос: как мы можем постепенно улучшить это, чтобы иметь хорошее всплывающее окно вместо новой страницы?

Что ж, первый шаг -создайте обработчик, чтобы открыть диалоговое окно, прикрепленное к ссылкам редактирования, по щелчку (обязательно e.PreventDefault).Теперь, чтобы сэкономить слишком много усилий по написанию кода, я хотел бы повторно использовать страницу редактирования.Это потребует некоторого рефакторинга, поскольку вы не хотите включать макет для запросов AJAX.Вы можете сделать это несколькими способами, но я думаю, что самое чистое - это иметь область редактирования представления редактирования как частичное представление, которое основное представление редактирования использует для визуализации своей модели.

Затем в действии редактирования, вы можете проверить, есть ли у вас ajax-запрос, если это так, то вернуть PartialView (mypartialeditview) или View (editview), если нет.

С точки зрения последующей отправки результатов на сервер, если вы хотителегкая жизнь, просто относись к ней как к форме.Вы можете использовать micorsoft unobstrive ajax здесь, и это будет очень просто.Вы используете Ajax.BeginForm в вашем частичном представлении.nbЭто ухудшится до нормальной формы, если ajax недоступен.Имейте AjaxOptions для этой beginform, чтобы обновить div в диалоге, поэтому, если он отвечает html, вам больше ничего не нужно делать, это подразумевает ошибку проверки.

[small asideВы спрашивали выше об этом: С точки зрения обработчика HttpPost, связыватель модели модели по умолчанию удивительно умен, он может привязывать поля формы к свойствам параметра объекта сложного класса.Это также работает с json, поэтому вам не нужно заканчивать множеством методов действия для поддержки различных сценариев.]

Так что, если ваше обновление окажется неудачным, обработчик постов снова вернет частичное представление,привязан к модели, так что вы получите все свои валидаторы.Однако, если обновление прошло успешно, я бы предложил вместо того, чтобы что-то возвращать, действие перенаправит вас обратно на вашу главную страницу, которую вы все равно хотите перезагрузить, так как вы изменили базовую версию.

Если вы этого не сделаетекак если бы вы делали полную перезагрузку главной страницы, то она становится более сложной, так как при вышеописанном подходе вы возвращаете html.Вам нужно будет либо выполнить jquery, чтобы найти скрытое поле или класс, чтобы указать на успех / неудачу, либо перейти на чистый подход json, который возвращает jsonresult.Это становится все тяжелее на фронте обслуживания / кодирования.Я, вероятно, сделал бы проверку jquery и связал бы ее с обработчиком завершения ajax.Beginform.

Если вы действительно хотите разобраться с этим, я считаю книгу Steve Sanderson Pro Asp.net MVC бесценной.Тот, который я первоначально прочитал, был для MVC2, но только в процессе чтения обновления MVC3.Я испытываю смешанные чувства по поводу обновления, поскольку в некоторых местах оно было упрощено - теперь легче следить за ним, но я чувствую, что некоторые вещи отсутствуют, к тому же оно было спешно вызвано ошибками и т. Д., Когда оно приближалось к концу.Я думаю, может быть, они запаниковали сейчас, когда говорят о MVC4, а книги не было!Тем не менее, это хорошая книга, и она прекрасно описывает все эти вещи.

Надеюсь, это поможет.Ценю, что он охватывает те же вопросы, что и ответ выше, но надеюсь, что я подробно рассказал вам.

2 голосов
/ 13 октября 2011

Я думаю, что лучший способ - это нормально отобразить список.Подключите редактируемые ссылки, чтобы перейти на отдельную страницу (следуйте за мной здесь), как вы обычно делаете.

С помощью JS обрабатывайте нажатие на ссылку и переходите к ее ссылке.В действии редактирования выполните проверку для Request.IsAjaxRequest() и, если это так, верните частичное представление, если это не так, верните полное представление.Или визуализируйте обычный вид редактирования без главной страницы (передайте значение null параметру главной страницы в вызове View () или вызовите возврат Partial ()).Возьмите содержимое результата и поместите его в диалоговое окно.

Также используйте JS для обработки отправки формы и получения результата из запроса.Если это не удалось, вставьте содержимое представления в диалоговое окно, чтобы показать, что были ошибки.В противном случае закройте его и двигайтесь дальше.

Преимущество этого подхода в том, что он очень ненавязчив и по-прежнему предоставляет функциональность тем, у кого нет JS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...