Atributos HTML condicionais usando Razor MVC3

100

A variável strCSSClass geralmente tem um valor, mas às vezes está vazia.

Não quero incluir um class = "" vazio no HTML deste elemento de entrada, o que significa que se strCSSClass estiver vazio, não quero o atributo class = de forma alguma.

A seguir está uma maneira de fazer um atributo HTML condicional:

<input type="text" id="@strElementID" @(CSSClass.IsEmpty() ? "" : "class=" + strCSSClass) />

Existe uma maneira mais elegante de fazer isso? Especificamente um onde eu poderia seguir a mesma sintaxe usada nas outras partes do elemento: class = "@ strCSSClass"?

tony722
fonte

Respostas:

160

Você não ouviu isso de mim, o PM do Razor, mas no Razor 2 (Páginas da Web 2 e MVC 4) teremos atributos condicionais integrados ao Razor (a partir do MVC 4 RC testado com sucesso), então você pode apenas dizer coisas assim ...

<input type="text" id="@strElementID" class="@strCSSClass" />

Se strCSSClass for nulo, o atributo de classe não será renderizado.

SSSHHH ... não diga. :)

Erik Porter
fonte
1
Sim, você definitivamente não deveria aceitar a minha como resposta. Dito isso, hoje não existe uma maneira mais limpa de fazer isso (portanto, estamos criando uma maneira mais limpa de fazer isso). Provavelmente, a maneira mais limpa de fazer isso é usar Html.TextBox, mas isso tem um conjunto diferente de coisas menos desejáveis. :( Que bom que você gostou do que estamos adicionando. :)
Erik Porter
1
Mas como posso combinar os atributos do Razor com outro texto? Preciso fazer o seguinte: ... id = "[email protected]". Eu esperava algo como ... id = "track_2", mas gerou a seguinte saída: ... id = "[email protected]" ...
Laserson
2
Você pode forçar o Razor a avaliar o código colocando parênteses ao redor dele. id = "track _ @ (track.ID)"
Erik Porter
1
Adicionada nota indicando que isso funciona com sucesso com MVC 4. Se você estiver no MVC 3, veja minha resposta abaixo.
AaronLS
4
@ErikPorter POR FAVOR, NÃO SUJE COM MEU HTML !! consulte também stackoverflow.com/questions/9234467/… isto também para outros comportamentos
inadequados
126

Observe que você pode fazer algo assim (pelo menos em MVC3):

<td align="left" @(isOddRow ? "class=TopBorder" : "style=border:0px") >

O que eu acreditava que era uma navalha adicionando aspas era na verdade o navegador. Como Rism apontou ao testar com MVC 4 (não testei com MVC 3, mas presumo que o comportamento não mudou), isso realmente produz, class=TopBordermas os navegadores são capazes de analisar isso bem. Os analisadores HTML são um tanto tolerantes quanto à falta de aspas do atributo, mas isso pode falhar se você tiver espaços ou determinados caracteres .

<td align="left" class="TopBorder" >

OU

<td align="left" style="border:0px" >

O que há de errado em fornecer suas próprias cotações

Se você tentar usar algumas das convenções C # usuais para aspas aninhadas, você acabará com mais citações do que esperava, porque o Razor está tentando escapar delas com segurança. Por exemplo:

<button type="button" @(true ? "style=\"border:0px\"" : string.Empty)>

Isso deve avaliar como, <button type="button" style="border:0px">mas o Razor escapa de toda a saída do C # e, portanto, produz:

style=&quot;border:0px&quot;

Você só verá isso se visualizar a resposta na rede. Se você usar um inspetor de HTML, geralmente verá o DOM, não o HTML bruto. Os navegadores analisam o HTML no DOM, e a representação do DOM após a análise já tem algumas sutilezas aplicadas. Nesse caso, o navegador vê que não há aspas em torno do valor do atributo e os adiciona:

style="&quot;border:0px&quot;"

Mas no inspetor DOM, os códigos de caracteres HTML são exibidos corretamente para que você realmente veja:

style=""border:0px""

No Chrome, se você clicar com o botão direito e selecionar Editar HTML, ele voltará para que você possa ver aqueles códigos de caracteres HTML desagradáveis, deixando claro que você tem aspas externas reais e aspas internas codificadas em HTML.

Portanto, o problema de tentar fazer as citações por conta própria é que Razor escapa disso.

Se você deseja controle total das cotações

Use Html.Raw para evitar o escape de citação:

<td @Html.Raw( someBoolean ? "rel='tooltip' data-container='.drillDown a'" : "" )>

Renderiza como:

<td rel='tooltip' title='Drilldown' data-container='.drillDown a'>

O procedimento acima é perfeitamente seguro porque não estou gerando nenhum HTML de uma variável. A única variável envolvida é a condição ternária. No entanto, esteja ciente de que esta última técnica pode expô-lo a certos problemas de segurança se você estiver construindo strings a partir de dados fornecidos pelo usuário. Por exemplo, se você construiu um atributo a partir de campos de dados originados de dados fornecidos pelo usuário, o uso de Html.Raw significa que a string pode conter um final prematuro do atributo e da tag, então comece uma tag de script que faz algo em nome do usuário atualmente conectado usuário (possivelmente diferente do usuário conectado). Talvez você tenha uma página com uma lista de todas as fotos de usuários e esteja definindo uma dica de ferramenta para ser o nome de usuário de cada pessoa e um usuário se autodenomina'/><script>$.post('changepassword.php?password=123')</script> e agora qualquer outro usuário que visualizar esta página terá sua senha alterada instantaneamente para uma senha que o usuário malicioso conhece.

AaronLS
fonte
Esse é um argumento muito bom! E, na verdade, torna-o legível e utilizável na maioria das situações.
Dmytro Shevchenko
Isso é o que eu estava fazendo no exemplo da minha pergunta. Obrigado por uma explicação e um exemplo mais elaborados. :)
tony722
1
Tenha cuidado com os espaços. "estilo = exibição: nenhum;" renderiza como nenhum; = "": = "" style = "display"
wmcainsh
1
Então, por que isso não funciona re: aspas duplas mágicas <span @ (true? "Class = logo-owner": string.Empty) /> Ele apenas produz <span class = logo-owner />
rism
1
@AaronLS Sim, é exatamente assim que eles são. Não consigo entender como o navegador (Chrome 40 / FF33.1 / IE 10) afetaria alguma coisa, uma vez que esta é uma marcação gerada pelo servidor e se sim, como vêm apenas esses dois atributos de classe, mas não para o atributo de classe do botão perguntar ou mesmo o tipo = Atributos de "botão" de todos os três botões. Definitivamente uma coisa de servidor IMHO, já que também posso RDP em algumas máquinas virtuais do Azure espalhadas pelo mundo para o mesmo resultado IE / Firefox / Chrome.
RISM
12

Eu acho que uma forma um pouco mais conveniente e estruturada é usar o helper Html. Em sua opinião, pode ser semelhante a:

@{
 var htmlAttr = new Dictionary<string, object>();
 htmlAttr.Add("id", strElementId);
 if (!CSSClass.IsEmpty())
 {
   htmlAttr.Add("class", strCSSClass);
 }
}

@* ... *@

@Html.TextBox("somename", "", htmlAttr)

Se esta forma for útil para você, eu recomendo definir o dicionário htmlAttrem seu modelo para que sua visualização não precise de @{ }blocos lógicos (seja mais claro).

Yaschur
fonte
4
-1, nunca recomende alguém para colocar lógica nas visualizações. As visualizações devem ser responsáveis ​​apenas pela renderização. Em vez disso, adicione um exemplo HtmlHelper e darei +1.
jgauffin
1
Para testar as coisas, posso usar o bloco de código em vista - é mais rápido. E minha recomendação foi mover este bloco para o modelo.
Yaschur
3
sim, mas as respostas devem mostrar as melhores práticas e não a versão rápida e suja que torna a manutenção de código um pesadelo.
jgauffin
@jgauffin Acho que o helper Html aqui é desnecessário. veja minha resposta.
gdoron está apoiando Monica em
+1 @Yaschur Sua resposta é inspiradora. Mantenha o bom trabalho. ie. com o recurso de conversão explícita do C #, podemos aprimorar ainda mais essa resposta. Nem todo o código teve que ser moldado de uma determinada maneira. Sempre há uma maneira melhor da qual não temos conhecimento. E alguns projetos são a favor da organização do código. A organização do código é, pela prática, não conceitos!
Bamboo