MVC4 Razor путают о брекетах

У меня довольно простой вопрос для всех экспертов Razor. Я пытаюсь сделать вызов jQuery $.ajax() для URL-адреса, используя Url.Content(), чтобы перевести исходный путь в корневой путь. При этом Razor немного запутывается, где находится конец моего @section. Я предпочел бы иметь возможность указывать URL-адрес в строке, но когда я это делаю, Razor считает, что конец списка параметров $.ajax() - это конец моего @section. Я использую @section, потому что хочу использовать макеты для размещения javascript в нижней части каждого файла. Почему Бритва так запуталась? Я даже пытался использовать @(Url.Content(...)), но это тоже не работает.

Кроме того, это лучший способ подойти к проблеме? Я использую предварительный просмотр ASP.NET MVC 4.

Это работает:

@section Scripts {
    <script type="text/javascript">        
        var getMessagesUrl = '@Url.Content("~/Logging/GetMessages")';

        $(document).ready(function () {
            $.ajax({
                url: getMessagesUrl,
                dataType: 'html',
                success: function (result) {
                    $('tbody').html(result);
                }
            });
        });
    </script>
}

Это не:

@section Scripts {
    <script type="text/javascript">        
        $(document).ready(function () {
            $.ajax({
                url: '@Url.Content("~/Logging/GetMessages")',
                dataType: 'html',
                success: function (result) {
                    $('tbody').html(result);
                }
            }); //Razor thinks this is the curly-brace that ends the section!
        });
    </script>
}

Ответ 1

Спасибо за помощь, ребята.

Похоже, что это была ошибка в ASP.NET MVC 4 Preview. Я только что обновился до бета-версии ASP.NET MVC 4, который вышел 15 февраля, и проблема теперь полностью исчезла.

Ответ 2

Вероятно, это связано с поведением парсера. Когда он встречает символ @, синтаксический анализатор переключается в режим кода и будет читать неявное выражение Url.Content("~/Logging.GetMessages") (ну, на самом деле он будет читать до символа ', определите, что он не является допустимым символом в выражении и трекбеке чтобы вернуться до конца ). После этого этапа парсер немного запутался в вашем представлении, потому что он, скорее всего, в режиме кода, когда встречается с последним символом }, и считает, что это конец пробел кода.

Реальность такова, что вы должны быть очень осторожны при использовании javascript в режиме бритвы с С#. Переходы на код являются явными, например. @ и после {, но переходы на разметку немного сложнее определить.

В отличие от Razor, моя рекомендация заключалась бы в том, чтобы прикрепить код приложения javascript во внешний файл и использовать атрибуты data- * для передачи метаинформации в ваш код приложения, например:

<script id="messageScript" type="text/javascript"
    src="/Scripts/messages.js" 
    data-messages="@Url.Content("~/Logging/GetMessages")"></script>

к которому вы можете получить доступ:

(function($) {

    $(function() {
        var $this = $("#messageScript");
        $.ajax({
            url: $this.attr("data-messages"),
            type: "html",
            success: function(result) {
                $("tbody").html(result);
            }
        });
    });

})(window.jQuery);

Ответ 3

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

В MVC4 я смог изолировать проблему. Это не будет компилироваться:

<script type="text/javascript">
  $(document).ready(function () {
    $.ajax({
      url: '@Url.Content("~/Logging/GetMessages")',
      dataType: 'html',
      success: function (result) {
        $('tbody').html(result);
      }
    }); //<-- test
  });
</script>

Но это:

<script type="text/javascript">
  $(document).ready(function () {
    $.ajax({
      url: '@Url.Content("~/Logging/GetMessages")',
      dataType: 'html',
      success: function (result) {
        $('tbody').html(result);
      }
    }); //-- test
  });
</script>

Похоже, что это был только < в комментарии, который его бросал.

Ответ 4

Matthew отвечает в значительной степени объясняет поведение (хотя, честно говоря, я не могу воспроизвести вашу проблему - и не понимаю, почему это не сработает - оба примера работают здесь очень хорошо). Для другого подхода вы можете выделить действие/представление для сгенерированных переменных javascript (URL-адреса, настройки, локализованные тексты и т.д.), То есть:

// Could/should be OutputCached depending on the scenario
public ActionResult Globals()
{
   var model = new ClientGlobalsModel();

   // ClientGlobalsModel has a single (could be more) Dictionary<string, string>
   // (Urls) and a ToJSON() method which uses JavaScriptSerializer to serialize 
   // the object:
   model.Urls.Add("GetMessages", Url.Content("~/Logging/GetMessages"));

   // I mostly use this method for e.g. actions:
   model.Urls.Add("UploadImage", Url.Action("Upload", "Image"));

   Response.ContentType = "text/javascript";

   return View(model);
}

Globals.cshtml:

@model ClientGlobalsModel
@{
    Layout = null; // If you have a layout supplied in e.g. _ViewStart
}
var GLOBALS = @Model.ToJSON()

Да, это мог быть простой результат Content(), а не вид, но если у вас больше глобальных переменных (например, настроек + URL + тексты), вам может потребоваться более легкий контроль над выходом script и, возможно, сериализация каждого словарь индивидуально. Может также хотеть, чтобы пространство имен было заменено на переменную "GLOBALS" в каком-либо общем пространстве имен приложений, чтобы избежать загрязнения глобальной области.

(например.) Index.cshtml:

<script src="@Url.Action("Globals", "Client")"></script>
<script src="@Url.Content("~/Scripts/main.js")"></script>

... который просто включает вывод /Client/Globals. И "main.js", в который мы теперь переместили остальные ваши script:

main.js(статический):

$(document).ready(function () {
   $.ajax({
      url: GLOBALS.Urls.GetMessages,
      dataType: 'html',
      success: function (result) {
         $('tbody').html(result);
      }
   });
});

Вы можете, конечно, использовать один и тот же подход для вывода нескольких пользовательских/контекстно-зависимых параметров непосредственно в представление. Для нескольких URL или данных подход атрибута data- * может быть лучше в зависимости от ваших вкусов. Тем не менее, я не поклонник набивки тонны основных настроек в атрибутах на каждой странице HTML.

Ответ 5

Кажется, все еще проблема с окончательным MVC4/Visual Studio 2010, но вот мое исправление:

@section jQueryDocumentReady
{
  @{
  <text>
     // All the javascript and bracers you want here
  </text>
  }
}