Control visibility of view components in ASP.NET MVC 5 controller

8

I am refactoring a project in ASP.NET MVC 5, implementing some good practices and researching, it was mentioned that the use of if in the view (razor) is not the best practice, but I use it to show or hide some components (fields) according to the current user's permission, I am researching how to limit this by the controller so that the rendering of the view is performed, but I can not find a way to do that (I do not even know if it's really possible), I'm going put an example of a code from the current view:

    @if ((bool)Session["TipoChamadoVisivel"])
    {
        <div class="form-group">
            @Html.LabelFor(model => model.TipoChamado, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.RadioButtonFor(model => model.TipoChamado, "1")<label>&nbsp;Totvs RM</label>
                <label>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;</label>
                @Html.RadioButtonFor(model => model.TipoChamado, "2")<label>&nbsp;Outros</label>
                <p>@Html.ValidationMessageFor(model => model.TipoChamado, "", new { @class = "text-danger" })</p>
            </div>

        </div>
    }

    @if ((bool)Session["ObraVisivel"])
    {
        <div class="form-group">
            @Html.Label("Obra Destino", new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.ObraDestino, new SelectList(new prj_chamadosBRA.Repositories.ObraDAO().BuscarObrasPorUsuario(ViewBag.UserId), "IDO", "Descricao"), "-- Selecione a Obra --", new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.ObraDestino, "", new { @class = "text-danger" })
            </div>
        </div>
    }
    @if ((bool)Session["SetorVisivel"])
    {
        if (Session["PerfilUsuario"].ToString() != "1")
        {
            <div class="form-group">
                @Html.Label("Setor Destino", new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.DropDownListFor(m => m.SetorDestino, new SelectList(new prj_chamadosBRA.Repositories.SetorDAO().BuscarSetoresPorObra(ViewBag.SetorDestino), "Id", "Nome"), new { @class = "form-control" })
                    @Html.ValidationMessageFor(m => m.SetorDestino, "", new { @class = "text-danger validacaoSetor" })
                </div>
            </div>
        }
        else
        {
            <div class="form-group">
                @Html.Label("Setor Destino", new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.DropDownListFor(m => m.SetorDestino, new SelectList(string.Empty, "Id", "Nome"), "-- Selecione o Setor --", new { @class = "form-control" })
                    @Html.ValidationMessageFor(m => m.SetorDestino, "", new { @class = "text-danger" })
                </div>
            </div>
        }
    }

    @if ((bool)Session["SelecionarResponsavelAbertura"])
    {
        <div class="panel panel-default">
            <div class="panel-heading">
                <h4 class="panel-title">
                    <a data-toggle="collapse" data-parent="#accordion" href="#pnlResponsavelAbertura">Abrir chamado em nome de outro usuário?</a>
                </h4>
            </div>
            <div id="pnlResponsavelAbertura" class="panel-collapse collapse">
                <div class="panel-body">
                    @if (Session["PerfilUsuario"].ToString() != "1" && Session["PerfilUsuario"].ToString() != "4")
                    {
                        <div class="form-group">
                            @Html.Label("Selecione Usuario:", new { @class = "col-md-2 control-label" })
                            <div class="col-md-10">
                                @Html.DropDownListFor(m => m.ResponsavelAberturaChamado, new SelectList(new prj_chamadosBRA.Repositories.ApplicationUserDAO().retornarUsuariosObra(ViewBag.SetorDestino), "Id", "Nome"), "-- Selecione o usuário --", new { @class = "form-control" })
                                @Html.ValidationMessageFor(m => m.ResponsavelAberturaChamado, "", new { @class = "text-danger" })
                            </div>
                        </div>
                    }
                    else
                    {
                        <div class="form-group">
                            @Html.Label("Selecione Usuario:", new { @class = "col-md-2 control-label" })
                            <div class="col-md-10">
                                @Html.DropDownListFor(m => m.ResponsavelAberturaChamado, new SelectList(string.Empty, "Id", "Nome"), "-- Selecione a obra primeiro --", new { @class = "form-control" })
                                @Html.ValidationMessageFor(m => m.ResponsavelAberturaChamado, "", new { @class = "text-danger" })
                            </div>
                        </div>
                    }
                </div>
            </div>
        </div>
    }
    
asked by anonymous 21.09.2015 / 15:40

2 answers

4

View control is the responsibility of the same view. The controller has another responsibility within the MVC, which is to receive, interpret and direct the user's requests by responding and activating the controllers of each type of operation.

There is nothing wrong with using the razor IFs within the view, if you create a project from scratch with the microsoft login template you will see that they use ifs to show the login > or logout in the top corner according to the status of the model.

I really think the danger here is you access and make direct use of your session to do these validations.

Maybe it would be better within your Model to have a struct of views and then yes to make use of it in the view.

And then, in your controller, you manipulate what types of views this model has and then manipulate the view in the view.

Another tip is to use ViewData which is exactly a transient data type that only lasts between a request and view rendering.

If you want to improve the code and make it cleaner you can apply this along with the Partials partial rendering pattern

And putting the two together could be like this

@if((bool)ViewData["MostrarSecao1"]) { @{Html.RenderPartial("secao1", Model);} }
@if((bool)ViewData["MostrarSecao2")) { @{Html.RenderPartial("secao2", Model);} }

These ViewData are populated in your controller.

ViewData["MostrarSecao1"] = //condição para mostrar a seção 1;
ViewData["MostrarSecao2"] = //condição para mostrar a seção 2;

And your partials can stay in any project location (within clear views) and in them you can only have your code without the ifs. For example

<div class="form-group">
            @Html.Label("Obra Destino", new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.ObraDestino, new SelectList(new prj_chamadosBRA.Repositories.ObraDAO().BuscarObrasPorUsuario(ViewBag.UserId), "IDO", "Descricao"), "-- Selecione a Obra --", new { @class = "form-control" })
                @Html.ValidationMessageFor(m => m.ObraDestino, "", new { @class = "text-danger" })
            </div>
        </div>

This would be your code within the partial secao1.cshtml

    
21.09.2015 / 16:06
4

Using session variables in this way is quite dangerous:

@if ((bool)Session["TipoChamadoVisivel"]) { ... }

Even because you do not test the existence of this content before using it.

In addition, this way of using causes some visual pollution. The best way to resolve is to encapsulate the content through Partials , by first testing whether the session variable exists. For example, this snippet of code:

@if ((bool)Session["TipoChamadoVisivel"])
{
    <div class="form-group">
        @Html.LabelFor(model => model.TipoChamado, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.RadioButtonFor(model => model.TipoChamado, "1")<label>&nbsp;Totvs RM</label>
            <label>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;</label>
            @Html.RadioButtonFor(model => model.TipoChamado, "2")<label>&nbsp;Outros</label>
            <p>@Html.ValidationMessageFor(model => model.TipoChamado, "", new { @class = "text-danger" })</p>
        </div>

    </div>
}

Can be encapsulated within a Partial named like this:

@Html.Partial("_TipoChamado", Model).If(Session["TipoChamadoVisivel"] != null)

This If is an extension that can be implemented as follows:

public static class HtmlHelperExtensions
{

    /// <summary>
    /// Método de extensão para avaliar condições na View.
    /// </summary>
    /// <param name="valor"></param>
    /// <param name="avaliacao"></param>
    /// <returns></returns>
    public static IHtmlString If(this IHtmlString valor, bool avaliacao)
    {
        return avaliacao ? valor : MvcHtmlString.Empty;
    }

}

Another tip is to register the Namespace of extensions in web.config of the Views directory:

<configuration>
  ...
  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <!--pages pageBaseType="ServiceStack.Razor.ViewPage"-->
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Optimization" />
        <add namespace="System.Web.Routing" />
        <add namespace="System" />
        <add namespace="MeuProjeto" />
        <add namespace="MeuProjeto.Extensions" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
  ...
</configuration>

Partial would look like this:

@if ((bool)Session["TipoChamadoVisivel"]) // Agora você sabe que a variável existe, e pode usar sem problemas.
{
    <div class="form-group">
        @Html.LabelFor(model => model.TipoChamado, new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.RadioButtonFor(model => model.TipoChamado, "1")<label>&nbsp;Totvs RM</label>
            <label>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;</label>
            @Html.RadioButtonFor(model => model.TipoChamado, "2")<label>&nbsp;Outros</label>
            <p>@Html.ValidationMessageFor(model => model.TipoChamado, "", new { @class = "text-danger" })</p>
        </div>

    </div>
}
    
21.09.2015 / 15:56