MVC 3: Como renderizar uma exibição sem sua página de layout quando carregada via ajax?

153

Estou aprendendo sobre o aprimoramento progressivo e tenho uma pergunta sobre os modos de exibição AJAXifying. No meu projeto MVC 3, tenho uma página de layout, uma página de início de exibição e duas visualizações simples.

A página de início de exibição está na raiz da pasta Exibições e, portanto, se aplica a todas as exibições. Ele especifica que todas as visualizações devem ser usadas _Layout.cshtmlpara sua página de layout. A página de layout contém dois links de navegação, um para cada visualização. Os links usam @Html.ActionLink()para renderizar-se na página.

Agora eu adicionei o jQuery e quero seqüestrar esses links e usar o Ajax para carregar seu conteúdo na página dinamicamente.

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

Existem duas maneiras de pensar nisso, mas não gosto particularmente de nenhuma delas:

1) Posso pegar todo o conteúdo da Visualização e colocá-los em uma visualização parcial, e depois a visualização principal chama a visualização parcial quando é renderizada. Dessa forma, usando Request.IsAjaxRequest()no controlador, eu posso retornar View()ou retornar com PartialView()base no fato de a solicitação ser ou não Ajax. Não posso retornar a exibição regular à solicitação do Ajax porque ela usaria a página de layout e eu receberia uma segunda cópia da página de layout. No entanto, não gosto disso porque me obriga a criar visualizações vazias com apenas um @{Html.RenderPartial();}nelas para as solicitações GET padrão.

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

Em Index.cshtml, faça o seguinte:

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

2) Posso remover a designação de layout de _viewstart e especificá-la manualmente quando a solicitação NÃO for Ajax:

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

Alguém tem uma sugestão melhor? Existe uma maneira de retornar uma exibição sem sua página de layout? Seria muito mais fácil dizer explicitamente "não inclua seu layout" se for uma solicitação ajax, do que incluir explicitamente o layout se não for um ajax.

Chev
fonte

Respostas:

259

Em ~/Views/ViewStart.cshtml:

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

e no controlador:

public ActionResult Index()
{
    return View();
}
Darin Dimitrov
fonte
3
Isso pode ser especificado no viewstart?
Chev
10
@ Matt Greer, você chamá-lo desagradável, eu chamo-o seco, o material subjetiva de qualquer maneira :-)
Darin Dimitrov
2
Eu tenho que admitir, eu não gostei no começo, mas a quantidade de código que ele salva parece muito superior à sua desvantagem. É um booleano simples se e realmente não impõe muita IMO. Eu gosto mais do que cortar meus métodos de ação pela metade toda vez. Além disso, ele me impede de fazer o que você disse Matt e potencialmente percorrer dois caminhos lógicos gigantes no método de ação. Eu escrevo a ação para funcionar da mesma forma nos dois casos ou escrevo uma nova ação.
Chev
1
você não poderia fazer isso em um controlador base, definir uma propriedade no ViewData e usá-la? Então a linha seria Layout = ViewBag.LayoutFile.
RPM1984
2
Suponho que sim, mas realmente por que criar um baseController para uma pequena linha?
Chev
92

Basta colocar o seguinte código no topo da página

@{
    Layout = "";
}
roncansan
fonte
4
Isso não funciona porque eu quero poder ativar ou desativar o layout, dependendo se ele é ou não solicitado via AJAX. Isso permite apenas desativar o layout, não alterná-lo.
Chev
4
Por que isso tem votações? pls explica, então vou votar também.
Usman Younas
1
@UsmanY. Você não precisa votar. Mas eu sim. Meu argumento não está no google.com.pk/#q=mvc3%20view%20without%20layout . E é a resposta perfeita para essa consulta.
Sami
3
O tópico é sobre como alternar o layout em dois cenários diferentes. Essa resposta apenas define o layout para esvaziar, não importa qual seja o cenário.
Rajshekar Reddy 30/11/2013
Cara, isso funciona e é muito legal. O cenário que eu uso: O usuário não autorizado tenta fazer login, não queremos que a página de erro mostre links e assim por diante para um usuário não autorizado! Claro, funciona para todo o resto também!
JosephDoggie
13

Eu prefiro e uso sua opção nº 1. Não gosto do número 2 porque para mim View()implica que você está retornando uma página inteira. Deverá ser uma página HTML válida e completa, assim que o mecanismo de exibição estiver pronto. PartialView()foi criado para retornar pedaços arbitrários de HTML.

Não acho muito importante ter uma visão que apenas chame de parcial. Ainda está SECO e permite que você use a lógica do parcial em dois cenários.

Muitas pessoas não gostam de fragmentar os caminhos de chamada de suas ações Request.IsAjaxRequest(), e eu posso apreciar isso. Mas a IMO, se tudo o que você está fazendo é decidir se deseja ligar View()ou não PartialView(), o ramo não é grande coisa e é fácil de manter (e testar). Se você se encontra usando IsAjaxRequest()para determinar grandes partes de como sua ação é executada, é provável que fazer uma ação AJAX separada seja melhor.

Matt Greer
fonte
13

Crie dois layouts: 1. layout vazio, 2. layout principal e, em seguida, escreva no arquivo _viewStart este código:

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

claro, talvez não seja a melhor solução

Arash Karami
fonte
8

Você não precisa criar uma exibição vazia para isso.

No controlador:

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

retornar um PartialViewResult substituirá a definição de layout ao renderizar a resposta.

Souhaieb Besbes
fonte
2

Com o ASP.NET 5, não há mais variável de solicitação disponível. Você pode acessá-lo agora com Context.Request

Além disso, não há mais o método IsAjaxRequest (), você deve escrevê-lo sozinho, por exemplo, em 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");
        }
    }
}

Eu procurei por um tempo agora sobre isso e espero que ajude alguns outros também;)

Recurso: https://github.com/aspnet/AspNetCore/issues/2729

Drotak
fonte
-5

Para um aplicativo Ruby on Rails, consegui impedir o carregamento de um layout especificando render layout: falsena ação do controlador que eu queria responder com o ajax html.

user4381244
fonte
6
tags: c # asp.net, não ruby
MrKekson