MVC 3: Jak wyrenderować widok bez strony układu po załadowaniu przez Ajax?

153

Dowiaduję się o Progressive Enhancement i mam pytanie dotyczące widoków AJAXifying. W moim projekcie MVC 3 mam stronę układu, stronę Viewstart i dwa zwykłe widoki.

Strona Viewstart znajduje się w katalogu głównym folderu Views, a zatem ma zastosowanie do wszystkich widoków. Określa, że ​​wszystkie widoki powinny być używane _Layout.cshtmlna stronie układu. Strona układu zawiera dwa łącza nawigacyjne, po jednym dla każdego widoku. Linki służą @Html.ActionLink()do wyświetlania się na stronie.

Teraz dodałem jQuery i chcę przejąć te linki i użyć Ajax do dynamicznego ładowania ich zawartości na stronie.

<script type="text/javascript">
    $(function () {
        $('#theLink').click(function () {
            $.ajax({
                url: $(this).attr('href'),
                type: "GET",
                success: function (response) {
                    $('#mainContent').html(response);
                }
            });
            return false;
        });
    });
</script>

Są dwa sposoby, aby to zrobić, ale nie podoba mi się żaden z nich:

1) Mogę pobrać całą zawartość widoku i umieścić ją w częściowym widoku, a następnie wywołać widok częściowy w widoku głównym podczas renderowania. W ten sposób, używając Request.IsAjaxRequest()w kontrolerze, mogę zwrócić View()lub zwrócić w PartialView()zależności od tego, czy żądanie jest żądaniem Ajax. Nie mogę zwrócić zwykłego widoku do żądania Ajax, ponieważ wtedy użyłby on strony układu i dostałbym drugą kopię wstrzykniętej strony układu. Jednak nie podoba mi się to, ponieważ zmusza mnie to do tworzenia pustych widoków z tylko jednym @{Html.RenderPartial();}w nich dla standardowych żądań GET.

    public ActionResult Index()
    {
        if (Request.IsAjaxRequest())
            return PartialView("partialView");
        else
            return View();
    }

Następnie w Index.cshtml zrób to:

@{Html.RenderPartial("partialView");}

2) Mogę usunąć oznaczenie układu z _viewstart i określić je ręcznie, jeśli żądanie NIE jest Ajax:

    public ActionResult Index()
    {
        if (Request.IsAjaxRequest())
            return View(); // Return view with no master.
        else
            return View("Index", "_Layout"); // Return view with master.
    }

Czy ktoś ma lepszą sugestię? Czy istnieje sposób na zwrócenie widoku bez strony układu? O wiele łatwiej byłoby wyraźnie powiedzieć „nie dołączaj swojego układu”, jeśli jest to żądanie AJAX, niż byłoby jawnie dołączyć układ, jeśli nie jest to AJAX.

Chev
źródło

Odpowiedzi:

259

W ~/Views/ViewStart.cshtml:

@{
    Layout = Request.IsAjaxRequest() ? null : "~/Views/Shared/_Layout.cshtml";
}

aw kontrolerze:

public ActionResult Index()
{
    return View();
}
Darin Dimitrov
źródło
3
Czy można to określić na początku widoku?
Chev
10
@Matt Greer, nazywasz to paskudnym, ja nazywam to SUCHY, w każdym razie subiektywne :-)
Darin Dimitrov
2
Muszę przyznać, że na początku mi się to nie podobało, ale ilość kodu, który zapisuje, wydaje się znacznie przewyższać jego wadę. Jest to prosta wartość logiczna, jeśli tak naprawdę nie narzuca zbyt wiele IMO. Podoba mi się to bardziej niż rozdrabnianie moich metod działania za każdym razem o połowę. Dodatkowo uniemożliwia mi to, co powiedziałeś, Matt i potencjalnie zejście na dwie gigantyczne ścieżki logiczne w metodzie działania. Albo napiszę akcję, aby działała tak samo w obu przypadkach, albo napiszę nową akcję.
Chev
1
nie mógłbyś tego zrobić w kontrolerze podstawowym, ustawić właściwość w ViewData i użyć tego? Wtedy linia byłaby Layout = ViewBag.LayoutFile.
RPM1984
2
Chyba mógłbym, ale naprawdę po co tworzyć baseController dla jednej małej linii?
Chev
92

Po prostu umieść poniższy kod na górze strony

@{
    Layout = "";
}
roncansan
źródło
4
To nie działa, ponieważ chcę mieć możliwość włączania i wyłączania układu w zależności od tego, czy jest on żądany przez AJAX. Pozwala to tylko wyłączyć układ, a nie go przełączać.
Chev
4
Dlaczego ma to wpływ na głosowanie? proszę wyjaśnić, więc ja też to zagłosuję.
Usman Younas
1
@UsmanY. Nie musisz na to głosować. Ale ja tak. Mój argument nie trafił na google.com.pk/#q=mvc3%20view%20without%20layout . I jest to doskonała odpowiedź na to pytanie.
Sami
3
Temat dotyczy przełączania układu w dwóch różnych scenariuszach. Ta odpowiedź po prostu ustawia układ na pusty bez względu na scenariusz.
Rajshekar Reddy
Stary, to działa i jest naprawdę fajne. Scenariusz, którego używam: nieautoryzowany użytkownik próbuje się zalogować, nie chce się, aby strona błędu pokazywała linki i tak dalej nieautoryzowanemu użytkownikowi! Oczywiście działa również na wszystko inne!
JosephDoggie
13

Wolę i używam twojej opcji nr 1. Nie podoba mi się # 2, ponieważ dla mnie View()oznacza, że ​​zwracasz całą stronę. Powinna to być w pełni dopracowana i poprawna strona HTML po zakończeniu pracy silnika widoku. PartialView()został stworzony, aby zwracać dowolne fragmenty kodu HTML.

Nie sądzę, żeby to wielka sprawa mieć pogląd, który nazywa się tylko częściowym. Nadal jest SUCHY i pozwala na użycie logiki częściowej w dwóch scenariuszach.

Wiele osób nie lubi fragmentować ścieżek połączeń swoich działań Request.IsAjaxRequest()i potrafię to docenić. Ale IMO, jeśli wszystko, co robisz, to decyzja, czy zadzwonić, View()czy PartialView()też oddział nie jest wielkim problemem i jest łatwy w utrzymaniu (i testowaniu). Jeśli okaże się, że używasz IsAjaxRequest()do określania dużej części tego, jak rozgrywa się Twoja akcja, utworzenie oddzielnej akcji AJAX jest prawdopodobnie lepsze.

Matt Greer
źródło
13

Utwórz dwa układy: 1. pusty układ, 2. main layout, a następnie zapisz w pliku _viewStart ten kod:

@{
if (Request.IsAjaxRequest())
{
    Layout = "~/Areas/Dashboard/Views/Shared/_emptyLayout.cshtml";
}
else
{
    Layout = "~/Areas/Dashboard/Views/Shared/_Layout.cshtml";
}}

oczywiście może to nie jest najlepsze rozwiązanie

Arash Karami
źródło
8

Nie musisz w tym celu tworzyć pustego widoku.

W kontrolerze:

if (Request.IsAjaxRequest())
  return PartialView();
else
  return View();

zwrócenie PartialViewResult spowoduje przesłonięcie definicji układu podczas renderowania odpowiedzi.

Souhaieb Besbes
źródło
2

W ASP.NET 5 nie jest już dostępna zmienna żądania. Możesz teraz uzyskać do niego dostęp za pomocą Context.Request

Również nie ma już metody IsAjaxRequest (), musisz napisać ją samodzielnie, na przykład w Extensions \ HttpRequestExtensions.cs

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Mvc
{
    public static class HttpRequestExtensions
    {
        public static bool IsAjaxRequest(this HttpRequest request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            return (request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest");
        }
    }
}

Szukałem teraz tego przez chwilę i mam nadzieję, że niektórym też pomoże;)

Zasób: https://github.com/aspnet/AspNetCore/issues/2729

Drotak
źródło
-5

W przypadku aplikacji Ruby on Rails udało mi się zapobiec ładowaniu układu, określając render layout: falsew akcji kontrolera, że ​​chcę odpowiedzieć za pomocą ajax html.

user4381244
źródło
6
tagi: c # asp.net, a nie ruby
MrKekson