Загрузка пользовательских шаблонов ASP.Net MVC2 через Ajax и обновление модели - PullRequest
2 голосов
/ 23 июля 2010

У меня есть модель вида с набором других объектов.

public ParentViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<ChildViewModel> Child { get; set; } 
}

public ChildViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

В одном из моих представлений я передаю ParentViewModel в качестве модели, а затем использую

<%: Html.EditorFor(x => x) %>

Которые отображают форму для свойств Id и Name.

Когда пользователь нажимает кнопку, я вызываю действие через Ajax для загрузки в частичном представлении, которое принимает коллекцию Child:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>
<%: Html.EditorFor(x => x) %>

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

Проблема, с которой я столкнулся, заключается в том, что форма, созданная с помощью пользовательского шаблона Child, не использует соглашения об именах, используемые DefaultModelBinder.

то есть имя поля (при загрузке Ajax):

[0].FirstName

вместо:

Child[0].FirstName

Итак, действие Edit в моем контроллере:

[HttpPost]
public virtual ActionResult Edit(int id, FormCollection formValues)
{
    ParentViewModel parent = new ParentViewModel();
    UpdateModel(parent);

    return View(parent);
}

воссоздать ParentViewModel из отправленной формы не работает.

Мне интересно, как лучше всего выполнить загрузку в пользовательских шаблонах через Ajax, а затем использовать UpdateModel.

Ответы [ 3 ]

9 голосов
/ 23 июля 2010

Начнем с того, что вам нужно помнить, что ModelBinder по умолчанию является рекурсивным, и он попытается решить, что ему нужно сделать ... так что это довольно умно.Еще одна вещь, которую нужно помнить, это то, что вам не нужно , чтобы использовать помощники html, фактический html также отлично работает: -)

Итак, во-первых, с моделью, здесь ничего не отличается ...

public class ParentViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<ChildViewModel> Child { get; set; }
}

public class ChildViewModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

Родительское частичное представление - это принимает экземпляр ParentViewModel

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ParentViewModel>" %>

<h2>Parent</h2>
<%: Html.TextBox("parent.Name", Model.Name) %>
<%: Html.Hidden("parent.Id", Model.Id) %>

<% foreach (ChildViewModel childViewModel in Model.Child)
{
    Html.RenderPartial("Child", childViewModel);         
}
%>

Дочернее частичное представление - это принимает один экземпляр ChildViewModel

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ChildViewModel>" %>

<h3>Child</h3>
<%: Html.Hidden("parent.Child.index", Model.Id) %>
<%: Html.Hidden(string.Format("parent.Child[{0}].Id", Model.Id), Model.Id)%>
<%: Html.TextBox(string.Format("parent.Child[{0}].FirstName", Model.Id), Model.FirstName) %>

В этой точке следует отметить, что значение индекса - это то, что используется для обработки уникальной записи в списке.Это не обязательно должно быть инкрементное значение.

Итак, как вы это называете?Хорошо в действии Index, которое будет отображать данные, которые необходимо передать. Я настроил некоторые демонстрационные данные и вернул их в словарь ViewData для представления индекса.

Итак, действие контроллера ...

public ActionResult Index()
{
    ViewData["Message"] = "Welcome to ASP.NET MVC!";

    ViewData["Parent"] = GetData();

    return View();
}

private ParentViewModel GetData()
{
    var result = new ParentViewModel
                     {
                         Id = 1,
                         Name = "Parent name",
                         Child = new List<ChildViewModel>
                                     {
                                         new ChildViewModel {Id = 2, FirstName = "first child"},
                                         new ChildViewModel {Id = 3, FirstName = "second child"}
                                     }
                     };
    return result;
}

В реальном мире вы вызываете службу данных и т. Д.

И, наконец, содержимое представления индекса:

<form action="<%: Url.Action("Edit") %>" method="post">
    <% if (ViewData["Parent"] != null)  { %>

        <%
            Html.RenderPartial("Parent", ViewData["Parent"]); %>

    <% } %>
    <input type="submit" />
</form>

Сохранение

Итак, теперь у нас есть данные, отображаемые как мы можем вернуть их в действие?Что ж, это то, что механизм связывания моделей по умолчанию сделает для вас на простых типах данных в относительно сложных формациях.Таким образом, вы можете установить основной формат действия, которое вы хотите опубликовать как:

[HttpPost]
public ActionResult Edit(ParentViewModel parent)
{

}

Это даст вам обновленные данные с оригинальными идентификаторами (из скрытых полей), чтобы вы могли обновлять / редактироватьпри необходимости.

Новые дети через Ajax

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

Если это так, вы делаете что-то вроде этого ...

Добавить действие - Нужно действие, которое возвратит новую ChildViewModel

[HttpPost]
public ActionResult Add()
    {
        var result = new ChildViewModel();
        result.Id = 4;
        result.FirstName = "** to update **";
        return View("Child", result);
    }

Я дал ему идентификатор для простых демонстрационных целей.

Затем вам понадобится способ вызова кода, поэтому единственное представление, которое вам нужно обновить, - это главное представление Index.Это будет включать JavaScript, чтобы получить результат действия, ссылку для вызова кода и целевой HTML-тег для HTML, который будет добавлен.Также не забудьте добавить ссылку на jQuery на главной странице или в верхней части представления.

Представление указателя - обновлено!

<script type="text/javascript">

   function add() {

        $.ajax(
            {
                type: "POST",
                url: "<%: Url.Action("Add", "Home") %>",
                success: function(result) {
                    $('#newchild').after(result);
                },
                error: function(req, status, error) {

                }
        });
    }

</script>

<form action="<%: Url.Action("Edit") %>" method="post">
    <% if (ViewData["Parent"] != null)  { %>

        <%
            Html.RenderPartial("Parent", ViewData["Parent"]); %>

    <% } %>
    <div id="newchild"></div>

    <br /><br />
    <input type="submit" /> <a href="#" onclick="JavaScript:return add();">add child</a>
</form>

Этовызовет действие add и добавит ответ, когда он вернется в div newChild над кнопкой отправки.

Надеюсь, длинный пост полезен.

Наслаждайтесь: -)

2 голосов
/ 23 июля 2010

Хм ... лично я бы порекомендовал использовать результат JSON вместо результата HTML, чтобы вы поиграли на странице ...

делает систему чище.и твой постбэк работает; -)

1 голос
/ 27 июля 2010

Я нашел другой способ добиться этого, который работает в моей конкретной ситуации.

Вместо частичной загрузки через Ajax, которая строго типизируется для дочерней коллекции, например:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %>

Я создал строго типизированное представление для родительского типа и затем вызвал EditorFor в списке следующим образом:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Parent>" %>
<%: Html.EditorFor(x => x.ChildList) %>

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

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