Вопрос по asp.net-mvc – Как создать форму редактирования для объекта со свойством List в ASP.NET MVC 4 с помощью Razor

9

У меня есть страница редактирования для моего приложения MVC, используя Razor.

У меня есть модель, как:

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

    public string Name { get; set; }

    public string Description { get; set; }

    public List<MyOtherModelObject> OtherModelObjects { get; set; }
}

И MyOtherModelObject выглядит так:

public class MyOtherModelObject
{
    public string Name { get; set; }

    public string Description { get; set; }
}

Я делаю страницу редактирования для MyModelObject. Мне нужен способ добавить место в форме на странице «Правка» для MyModelObject, чтобы пользователь мог создать / добавить столько экземпляров MyOtherModelObject, сколько пользователь пожелает в Списке других объектов OtherModelObject.

Я думаю, что пользователь может нажать кнопку, которая выполнит ajax с другим действием, которое возвращает PartialView элементов формы (без тега формы, поскольку это предназначено для части формы на моей странице редактирования). Когда пользователь добавил все нужные объекты MyOtherModelObject и заполнил данные, он должен иметь возможность сохранить свои изменения в существующем объекте MyModelObject, что приведет к переходу HttpPost в действие Edit и, надеюсь, все объекты MyOtherModelObject будут в правильном списке.

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

Кто-нибудь знает, как заставить это работать? Реализован ли образец проекта или пошаговое руководство с этим решением?

На первый взгляд. Похоже, что он будет работать для списка, но будет ли он работать для списка, который является частью другого объекта, и сможет ли он связать модель? David Hollowell - MSFT
Вы могли бы посмотреть наhaacked.com/archive/2008/10/23/model-binding-to-a-list.aspx (хотя это может быть синтаксис Razor или MVC4, это может дать представление) Mister Henson

Ваш Ответ

2   ответа
0

Я смог взять урок, извлеченный из этого поста в блогеhttp://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ и примените его к моему случаю, когда ModelObject имеет несколько свойств, многие из которых являются List.

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

19

этоСообщение блога содержит пошаговое руководство, иллюстрирующее, как этого добиться.

ОБНОВИТЬ:

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

Модель:

public class MyOtherModelObject
{
    public string Name { get; set; }
    public string Description { get; set; }
}

public class MyModelObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public List<MyOtherModelObject> OtherModelObjects { get; set; }
}

контроллер:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyModelObject
        {
            Id = 1,
            Name = "the model",
            Description = "some desc",
            OtherModelObjects = new[]
            {
                new MyOtherModelObject { Name = "foo", Description = "foo desc" },
                new MyOtherModelObject { Name = "bar", Description = "bar desc" },
            }.ToList()
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyModelObject model)
    {
        return Content("Thank you for submitting the form");
    }

    public ActionResult BlankEditorRow()
    {
        return PartialView("EditorRow", new MyOtherModelObject());
    }
}

Посмотреть (~/Views/Home/Index.cshtml):

@model MyModelObject

@using(Html.BeginForm())
{
    @Html.HiddenFor(x => x.Id)
    <div>
        @Html.LabelFor(x => x.Name)
        @Html.EditorFor(x => x.Name)
    </div>
    <div>
        @Html.LabelFor(x => x.Description)
        @Html.TextBoxFor(x => x.Description)
    </div>
    <hr/>
    <div id="editorRows">
        @foreach (var item in Model.OtherModelObjects)
        {
            @Html.Partial("EditorRow", item);
        }
    </div>
    @Html.ActionLink("Add another...", "BlankEditorRow", null, new { id = "addItem" })

    <input type="submit" value="Finished" />
}

Частичный (~/Views/Home/EditorRow.cshtml):

@model MyOtherModelObject

<div class="editorRow">
    @using (Html.BeginCollectionItem("OtherModelObjects"))
    {
        <div>
            @Html.LabelFor(x => x.Name)
            @Html.EditorFor(x => x.Name)
        </div>
        <div>
            @Html.LabelFor(x => x.Description)
            @Html.EditorFor(x => x.Description)
        </div>
        <a href="#" class="deleteRow">delete</a>
    }
</div>

Автор сценария:

$('#addItem').click(function () {
    $.ajax({
        url: this.href,
        cache: false,
        success: function (html) {
            $('#editorRows').append(html);
        }
    });
    return false;
});

$('a.deleteRow').live('click', function () {
    $(this).parents('div.editorRow:first').remove();
    return false;
});

Замечание:BeginCollectionItem Пользовательский помощник взят из той же статьи, на которую я ссылался, но я предлагаю ее здесь для полноты ответа:

public static class HtmlPrefixScopeExtensions
{
    private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
    {
        var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
        string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();

        // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
        html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));

        return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
    }

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
        return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
    {
        // We need to use the same sequence of IDs following a server-side validation failure,  
        // otherwise the framework won't render the validation error messages next to each item.
        string key = idsToReuseKey + collectionName;
        var queue = (Queue<string>)httpContext.Items[key];
        if (queue == null)
        {
            httpContext.Items[key] = queue = new Queue<string>();
            var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
            if (!string.IsNullOrEmpty(previouslyUsedIds))
                foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
                    queue.Enqueue(previouslyUsedId);
        }
        return queue;
    }

    private class HtmlFieldPrefixScope : IDisposable
    {
        private readonly TemplateInfo templateInfo;
        private readonly string previousHtmlFieldPrefix;

        public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
        {
            this.templateInfo = templateInfo;

            previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
            templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
        }

        public void Dispose()
        {
            templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
        }
    }
}
@DaveH, но это было бы легко реализовать. Все, что вам нужно сделать, это ввести новую модель представления, которая имеетIEnumerable<T> свойство списка, который вы пытаетесь редактировать, точно так же, как у вас в вопросе сMyModelObject.
Хорошо, по-видимому, мне придется кодировать его для вас шаг за шагом. Подожди секунду.
Не совсем. В нем обсуждается, как сделать список объектов переменной длины, но не обсуждается, как это сделать для списка объектов переменной длины, которые являются частью другого объекта. David Hollowell - MSFT
Обыкновенно легко сказать? И как все это будет привязано к родительскому объекту MyModelObject. Остается решить большую часть ответа, который может включать переписывание поведения ModelBind по умолчанию в MVC. Есть ли проблемы безопасности с этим. Я работаю с этим образцом и нашел его в нескольких других сообщениях на форуме для решения списка отдельных объектов, но пока не совсем видел, как пример, который я дал, решен эффективно. David Hollowell - MSFT
Пожалуйста, смотрите мой обновленный ответ. Как видите, код очень близок к показанному в статье. Нет необходимости в переписывателе моделей :-) Все, что вам нужно было сделать, это подключить вашу модель. В следующий раз, пожалуйста, приложите немного больше усилий.

Похожие вопросы