Реплицированное поведение вложенных повторителей в MVC - PullRequest
1 голос
/ 03 декабря 2010

Сегодня я решил попробовать MVC, и, хотя мне действительно нравится эта идея, мне было довольно сложно перейти от ASP.NET и понять некоторые базовые концепции, такие как использование foreach вместо вложенных повторителей.

Мне понадобилось несколько часов, чтобы придумать это решение, но оно не совсем верно. Может кто-нибудь объяснить, что не так с этим кодом, и как это правильно сделать. Вот мое решение:

По сути, это опрос, который состоит из нескольких вопросов, на каждый из которых есть несколько ответов. У меня есть таблицы в БД, которые представлены как строго типизированные сущности. Контроллер выглядит так:

public ActionResult Details(int id)
{
    return View(new Models.Entities().Questions.Where(r => r.PROMId == id));
}

и соответствующий вид, подобный этому:

<% foreach (var question in Model) { %>

    <h3>Question <%: Array.IndexOf(Model.ToArray(), question) + 1 %></h3>
    <p><%: question.QuestionPart1 %></p>
    <p><%: question.QuestionPart2 %></p>
    <% var answers = new Surveys_MVC.Models.Entities().Answers.Where(r => r.QuestionId == question.QuestionId); %>
    <% foreach (var answer in answers) { %>
        <input type="radio" /><%: answer.Text %>
    <% } %>

<% } %>

Все отзывы приветствуются.

Ответы [ 2 ]

2 голосов
/ 04 декабря 2010

Что касается использования циклов for для поведения вложенных повторителей, я думаю, что это лучший способ сделать это в MVC. Но я бы посоветовал вам использовать специальные ViewModels.

ViewModel:

public class RadioQuestionListViewModel
{
    public IEnumerable<RadioQuestionViewModel> Questions {get;set;}
}

public class RadioQuestionViewModel
{
    public int QuestionNumber {get;set;}
    public string InputName {get;set;}
    public string QuestionPart1 {get;set;}
    public string QuestionPart2 {get;set;}
    public IEnumerable<RadioAnswerViewModel> PossibleAnswers {get;set;}
}
public class RadioAnswerViewModel
{
    public int AnswerId {get;set;}
    public string Text {get;set;}
}

Контроллер:

public ActionResult Details(int id)
{
    var model = GetRadioQuestionListModelById(id);
    return View(model);
}

Вид:

<% foreach (var question in Model) { %>

    <h3>Question <%: question.QuestionNumber %></h3>
    <p><%: question.QuestionPart1 %></p>
    <p><%: question.QuestionPart2 %></p>
    <% foreach (var answer in question.PossibleAnswers) { %>
        <%: Html.RadioButton(question.InputName, answer.AnswerId) %>
        <%: answer.Text %>
    <% } %>
<% } %>

Этот подход имеет несколько преимуществ:

  1. Он не позволяет вашему коду просмотра зависеть от ваших классов доступа к данным. Код представления должен отвечать только за принятие решения о том, как требуемая модель представления будет отображаться в HTML.
  2. Он не допускает никакой логики, связанной с отображением, в вашем коде представления. Если вы позже решите разместить свои вопросы на странице и теперь будете показывать вопросы 11-20 вместо 1 - что угодно, вы можете использовать точно такое же представление, потому что контроллер позаботился о поиске номеров вопросов для отображения.
  3. Упрощается избегать выполнения Array.IndexOf(Model.ToArray(), question) и обращения к базе данных внутри цикла for, что может стать довольно дорогостоящим, если у вас есть несколько вопросов на странице.

И, конечно, ваши радиокнопки должны иметь имя и значение ввода, связанные с ними, иначе вы не сможете получить эту информацию при отправке формы. Заставив контроллер решить, как генерируется входное имя, вы сделаете более очевидным, как метод Details соответствует вашему методу SaveAnswers.

Вот возможная реализация GetRadioQuestionListModelById:

public RadioQuestionListViewModel GetRadioQuestionListModelById(int id)
{
    // Make sure my context gets disposed as soon as I'm done with it.
    using(var context = new Models.Entities())
    {
        // Pull all the questions and answers out in a single round-trip
        var questions = context.Questions
            .Where(r => r.PROMId == id)
            .Select(r => new RadioQuestionViewModel
                {
                    QuestionPart1 = r.q.QuestionPart1,
                    QuestionPart2 = r.q.QuestionPart2,
                    PossibleAnswers = r.a.Select(
                        a => new RadioAnswerViewModel
                             {
                                AnswerId = a.AnswerId,
                                Text = a.Text
                             })
                })
            .ToList();
    }
    // Populate question number and name
    for(int i = 0; i < questions.Count; i++)
    {
        var q = questions[i];
        q.QuestionNumber = i;
        q.InputName = "Question_" + i;
    }
    return new RadioQuestionListViewModel{Questions = questions};
}
0 голосов
/ 04 декабря 2010

Я не знаю, лучше ли это, но вы можете создать помощника, который сделает это за вас:

public static void Repeater<T>(this HtmlHelper html, IEnumerable<T> items, string cssClass, string altCssClass, string cssLast, Action<T, string> render)
        {
            if (items == null)
                return;
            var i = 0;
            foreach (var item in items)
            {
                i++;
                if (i == items.Count())
                    render(item, cssLast);
                else
                    render(item, (i % 2 == 0) ? cssClass : altCssClass);
            }
        }

Затем вы можете назвать его так:* Это имеет много силы, и выше, это просто общий пример.Вы можете прочитать больше здесь http://haacked.com/archive/2008/05/03/code-based-repeater-for-asp.net-mvc.aspx

...