Как заставить это представление Razor работать так же, как в ASPX?

#asp.net-mvc-3 #razor

#asp.net-mvc-3 #razor

Вопрос:

Я изо всех сил пытался получить частичное представление, работающее в Razor. Механизм просмотра не может понять приведенный ниже код, но это просто с помощью механизма просмотра ASPX. Кто-нибудь может показать мне, как заставить это работать с Razor? Обратите внимание, что я просто выписываю календарь, поэтому тег <tr> появляется в конце каждой недели. Первым признаком проблемы является то, что код Razor не будет форматироваться в редакторе VS, и он жалуется, что в блоке ‘while’ отсутствует закрывающая скобка. Я перепробовал все виды комбинаций, даже используя делегат. (Я думаю, что причиной проблемы может быть условный тег TR, потому что он выделяется как ошибка, потому что он не закрыт.)

Razor (не работает)

  <table class="calendarGrid">
    <tr class="calendarDayNames">
        <th>Monday</th>
        <th>Tuesday</th>
        <th>Wednesday</th>
        <th>Thursday</th>
        <th>Friday</th>
        <th>Saturday</th>
        <th>Sunday</th>
    </tr>
    @{
        var loopDate = gridStartDate;
    }
    @while (loopDate <= gridEndDate)
    {
        if (loopDate.DayOfWeek == DayOfWeek.Monday)
        {
            <tr class="calendarWeek">
        }
            <td class="calendarDay">
                <span class="calendarDayNumber">@loopDate.Day</span>
                @if (Model.AllCalendarDays.ContainsKey(loopDate.Date))
                {
                    foreach (var ev in Model.AllCalendarDays[loopDate.Date])
                    {
                        <span class="calendarEvent">@ev.Venue</span>
                    }
                }
            </td>
            @{ 
                loopDate = loopDate.AddDays(1);
                @if (loopDate.DayOfWeek == DayOfWeek.Monday)
                {
                    </tr>
                }
            }
    }
  

ASPX (работает)

   <table class="calendarGrid">
    <tr class="calendarDayNames">
        <th>Monday</th>
        <th>Tuesday</th>
        <th>Wednesday</th>
        <th>Thursday</th>
        <th>Friday</th>
        <th>Saturday</th>
        <th>Sunday</th>
    </tr>
    <% 
        var loopDate = gridStartDate;

        while (loopDate <= gridEndDate)
        {
            if (loopDate.DayOfWeek == DayOfWeek.Monday)
            {
    %>
    <tr class="calendarWeek">
        <%} %>
        <td class="calendarDay">
            <span class="calendarDayNumber">
                <%: loopDate.Day %></span>
            <% if (Model.AllCalendarDays.ContainsKey(loopDate.Date))
               {
                   foreach (var ev in Model.AllCalendarDays[loopDate.Date])
                   { %>
            <span class="calendarEvent">
                <%: ev.Venue %></span>
            <%  }
               } %>
        </td>
        <%  {
                loopDate = loopDate.AddDays(1);
                if (loopDate.DayOfWeek == DayOfWeek.Monday)
                { %>
    </tr>
    <% }
            }
        } %>
</table>
  

Рабочее решение в Razor основано на предложении модели представления @jgauffin и уродливом необработанном HTML-решении @dommer. В сочетании они почти эстетически приемлемы. 🙂

Модель представления теперь имеет итератор

     public IEnumerable<Tuple<DateTime, IList<CalendarEventDto>>> GridItems()
    {
        var loopDate = GridStartDate;
        while (loopDate <= GridEndDate)
        {
            yield return new Tuple<DateTime, IList<CalendarEventDto>>(loopDate.Date, AllCalendarDays[loopDate.Date]);
            loopDate = loopDate.AddDays(1);
        }
    }
  

Хорошо, кортеж ленив, но я, вероятно, создам другую модель для хранения более сложной информации о дате и событиях (IsPast / greyed и т. Д.).

Надоедливый вид

            @foreach (var item in Model.GridItems())
        {
            if (item.Item1.DayOfWeek == DayOfWeek.Monday)
            { 
                @Html.Raw("<tr class="calendarWeek">");
            }
            @Html.Raw("<td class="calendarDay">");
            @Html.Raw(string.Format("<span class="calendarDayNumber">{0}</span>", item.Item1.Day));
            foreach (var ev in item.Item2)
            {
                @Html.Raw(string.Format("<span class="calendarEvent">{0}</span>", Server.HtmlEncode(ev.Venue)));
            }
            @Html.Raw("</td>");
            if (item.Item1.DayOfWeek == DayOfWeek.Sunday)
            {
                @Html.Raw("</tr>");
            }
        }
  

Обратите внимание, что когда я переформатирую источник представления в VS, он становится явно разбитым на вкладки, причем оператор if имеет около 10 вкладок слева от него, но предупреждений о компиляции нет, и он делает то, что я хочу. Хотя это и не приятно, и не просто. Я думаю, что разработчики Razor должны обеспечить некоторую поддержку явного выделения и разбиения кода и разметки, чтобы, когда анализатор не может его однозначно проанализировать, мы могли сказать ему, что мы намеревались.

Решение @Andrew Nurse Эндрю работает над ASP.Net команда, создающая анализатор Razor!». Его решение работает нормально, но по-прежнему выдает предупреждения компилятора и, очевидно, сбивает с толку Visual Studio, потому что код нельзя переформатировать, не превратив в большой глобус на несколько строк:

        <tbody>
        @foreach (var calDay in Model.GridItems())
        {
            if (calDay.DayOfWeek == DayOfWeek.Monday)
            { 
                @:<tr class="calendarWeek">
            } 
            <td class="calendarDay">
                <span class="calendarDayNumber">@calDay.Day</span> 
                @foreach (var ev in calDay.CalendarEvents) 
                { 
                    <span class="calendarEvent">@ev.Venue</span> 
                } 
            </td> 
            if (calDay.DayOfWeek == DayOfWeek.Sunday)
            { 
                @:</tr>                 
            }
        }
    </tbody>
  

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

1. Какую ошибку вы получаете? Также: я бы переместил логику в viewmodel, чтобы сделать ее тестируемой.

2. Ошибка заключается в том, что, во-первых, оно не форматируется (я получаю предупреждения о компиляции), и если я его запускаю, в зависимости от того, как я меняю блоки, я либо вообще ничего не пропускаю, либо исключение «из памяти», потому что приращение переменной цикла не выполняется. Я думаю, что моя общая точка зрения заключается в том, что то, что очень легко и предсказуемо в ASPX, практически невозможно понять в Razor 🙂

3. В зависимости от того, как я его изменяю, я получаю ошибки компиляции Razor, такие как: «У TR нет закрывающего тега», «если блок не нуждается в открытии @ when code block», «отсутствует закрывающая скобка в блоке «while». И если я все это исправлю и скомпилирую, весь код «if block» будет выдан в виде необработанного текста, который браузер отобразит над таблицей.

4. На данный момент я отказался от этой проблемы, поскольку потерял на ней слишком много времени. Версия ASPX работает нормально. Кажется, что синтаксический анализ идет наперекосяк при открытии блока if с тегом <tr> . Я пытался обернуть его в текстовые теги и т. Д., Но ничего не работает. Я думаю, что рендеринг строк с использованием partial будет работать, но зачем вам это нужно?

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

Ответ №1:

Основными проблемами здесь были эти строки:

 if (loopDate.DayOfWeek == DayOfWeek.Monday)
{
    <tr class="calendarWeek">
}

...

@if (loopDate.DayOfWeek == DayOfWeek.Monday)
{
    </tr>
}
  

Проблема в том, что Razor использует теги для определения начала и конца разметки. Итак, поскольку вы не закрыли тег «tr» внутри первого if, он фактически не переключается обратно на код, поэтому он не видит «}» как код. Решение состоит в том, чтобы использовать «@:», который позволяет размещать строку разметки без учета тегов. Поэтому замена этих строк на this должна работать и быть более краткой, чем при использовании Html.Raw:

 if (loopDate.DayOfWeek == DayOfWeek.Monday)
{
    @:<tr class="calendarWeek">
}

...

@if (loopDate.DayOfWeek == DayOfWeek.Monday)
{
    @:</tr>
}
  

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

1. Хотя ваше решение работает нормально, компилятор и редактор VS по-прежнему сообщают о нем как об ошибочном (я вставил ваше решение в свой вопрос выше). Я получаю предупреждения компилятора о недопустимости текста между тегами tbody и tr. Предупреждения визуально выделяются в редакторе, и я не могу переформатировать документ, потому что весь код в конечном итоге упаковывается в 6 строк вместо примерно 15. Показательно, что пять опытных разработчиков MVC, включая одного из разработчиков Razor team, не смогли удовлетворительно решить эту простую проблему после нескольких попыток.

2. 1, о котором я не знал @: . Это @: просто сокращение для @Html.Raw( ) ?

3. @Dommer Любопытно, но я бы подумал об этом как о простой директиве, чтобы сказать: все, начиная отсюда и заканчивая концом строки, является разметкой. В отличие от Html.Raw, вы все равно можете использовать выражения «@» И т. Д. Внутри строки, Которую вы выводите с помощью «@:»

4. @Rob — Я посмотрел, как VS форматирует код, и вы определенно правы, там есть сбой. Редактор — это отдельная команда, поэтому я подниму вопрос с ними и посмотрю, сможем ли мы исправить это в следующей версии. Никаких обещаний, но я передам отзыв непосредственно команде редактора.

5. Спасибо за информацию, Эндрю, я не знал об использовании @: , и здорово, что ты собираешься передать проблему, которую выявил Роб. Результат круглый — за исключением того, что я потерял 15 очков из-за того, что мой правильный ответ был удален … 😀

Ответ №2:

Я бы перенес всю логику в viewmodel, которая оставляет следующий код на ваш взгляд:

 @while (Model.MoveNext())
{
    @Model.WeekHeader
        <td class="calendarDay">
            <span class="calendarDayNumber">@Model.DayNumber</span>
            @foreach (var ev in Model.CurrentDayEvents)
            {
                <span class="calendarEvent">@ev.Venue</span>
            }
        </td>
    @Model.WeekFooter
}
  

И новая модель:

 public class CalendarViewModel
{
    private DateTime _currentDate;

    public string WeekHeader
    {
        get
        {
            return _currentDate.DayOfWeek == DayOfWeek.Monday ? "<tr class="calendarWeek">" : "";
        }
    }
    public string WeekFooter
    {
        get
        {
            return _currentDate.DayOfWeek == DayOfWeek.Monday ? "</tr>" : "";
        }
    }

    public IEnumerable<DayEvent> 
    { 
        get 
        { 
            return AllCalendarDays.ContainsKey(loopDate.Date) ? AllCalendarDays[loopDate.Date] ? new List<DayEvent>();
        }
    }

public bool MoveNext()
{
    if (_currentDate == DateTime.MinValue)
    {
        _currentDate = gridStartDate;
        return true;
    }

    _currentDate = _currentDate.AddDays(1);
    return _currentDate <= gridEndDate;
}
}
  

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

1. Это интересно, но я не думаю, что код представления (HTML) должен быть в модели представления. Кроме того, я не думаю, что это решает проблему, заключающуюся в том, что html теперь недействителен, потому что в представлении нет TR, что, по-видимому, вызывает проблему.

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

3. ViewModels был создан для удаления логики из представлений. вы не получите <tr>, если начальная дата не является понедельником

4. Удалите логику из представления, но не помещайте представление в ViewModel 🙂 — разделение проблем. Однако мне нравится ваша идея MoveNext. Однако проблема здесь не в том, что должно быть в View / ViewModel, а в сложности смешивания кода и html при использовании Razor и капризах интерпретатора. Я написал версию ASPX за 5 минут и потерял 12 часов, делая версию Razor, которая все еще не работает, независимо от того, какие варианты я использую. На мой взгляд, это означает, что в Razor есть что-то фундаментально сломанное или чрезмерно сложное.

5. razor работает нормально, если вы не вкладываете в него слишком много логики 🙂 Используйте <text></text> блоки вокруг html, чтобы сообщить razor, что вы переключились с кода на обычный HTML

Ответ №3:

ОСНОВНОЕ РЕДАКТИРОВАНИЕ: Хорошо, что произойдет, если вы это сделаете?

 <table class="calendarGrid">
<tr class="calendarDayNames">
    <th>Monday</th>
    <th>Tuesday</th>
    <th>Wednesday</th>
    <th>Thursday</th>
    <th>Friday</th>
    <th>Saturday</th>
    <th>Sunday</th>
</tr>
@{
var loopDate = gridStartDate;
while (loopDate <= gridEndDate)
{
    if (loopDate.DayOfWeek == DayOfWeek.Monday)
    {
        @Html.Raw("<tr class="calendarWeek">");
    }
    @Html.Raw("<td class="calendarDay">");
    @Html.Raw("<span class="calendarDayNumber">"   loopDate.Day   "</span>");
    if (Model.AllCalendarDays.ContainsKey(loopDate.Date))
    {
        foreach (var ev in Model.AllCalendarDays[loopDate.Date])
        {
            @Html.Raw("<span class="calendarEvent">"   ev.Venue   "</span>");
        }
    }
    @Html.Raw("</td>");
    loopDate = loopDate.AddDays(1);
    if (loopDate.DayOfWeek == DayOfWeek.Monday)
    {
        @Html.Raw("</tr>");
    }
}
}
  

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

1. Спасибо. Я попробовал этот вариант, но та же проблема. Я уверен, что это условные элементы TR, вызывающие проблему.

2. Хорошо, попробовал это. Я получаю исключение нехватки памяти, потому что переменная цикла не увеличивается, что означает, что она должна отображаться как текст.

3. Попробуйте обычные круглые скобки вокруг увеличивающегося бита, как в моей правке. Или попробуйте @loopDate = @loopDate.AddDays(1) . Я также не думаю, что вам нужна точка с запятой, не так ли? Опять же, я далеко от VS2010, поэтому я не могу попробовать это. Извините.

4. Пробовал оба из них. Проблема, похоже, начинается с открытия TR. Я вижу, что он не может проанализировать его из-за того, что он дает мне с intellisense, то есть он предлагает мне теги html TR внутри блоков кода. Сейчас я отказываюсь от этой проблемы и собираюсь сделать это одно представление в ASPX.

5. Это моя последняя попытка! Наконец-то я попробовал это в VS2010, и, похоже, оно должно работать (компилируется нормально), но я, очевидно, не запускал его…

Ответ №4:

Вы пробовали добавлять <text> теги вокруг содержимого блоков?

Кроме того, Razor также позволяет использовать текстовый элемент для явной идентификации содержимого

Я думаю, что анализ Razor работает только тогда, когда очевидно, где заканчиваются блоки. Возможно, вас смущает тот факт, что у вас есть if , a td , а затем еще немного кода, все внутри блока.

Здесь больше информации об этом: http://weblogs.asp.net/scottgu/archive/2010/12/15/asp-net-mvc-3-razor-s-and-lt-text-gt-syntax.aspx

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

1. Спасибо. Я попробую это и уже видел текстовый элемент раньше. Но я отмечаю это: «Это делает его удобным, когда вы хотите отображать многострочные блоки содержимого, которые не обернуты HTML-элементом». Поскольку я использую html-элементы, это не должно применяться, но я попробую.

2. Я думаю, что это описание чрезмерно упрощено. Я думаю, это применимо, когда весь контент не заключен в HTML-элемент.

3. Хорошо. Попробовал текстовые блоки, но затем он жалуется на отсутствие конечных тегов для текста. Кажется, что TR сбивает с толку интерпретатор.

4. Кроме того, согласно моей книге «Professional ASP MVC 3», текстовый тег используется только для обычного текста, а не для разметки. Я думаю, что все мои проблемы связаны с незакрытым тегом TR, потому что одно из предупреждений, которые я получаю, это «текст, не разрешенный внутри тега TR», что доказывает, что закрывающая скобка } этого блока обрабатывается как текст.

5. <text> Тег специально позволяет вам иметь теги, которые не совпадают — это действительно один из основных моментов, поэтому вы можете делать такие вещи, как: if (blah) { <tr> }