Warunkowe atrybuty HTML przy użyciu Razor MVC3

100

Zmienna strCSSClass często ma wartość, ale czasami jest pusta.

Nie chcę umieszczać pustej class = "" w kodzie HTML tego elementu wejściowego, co oznacza, że ​​jeśli strCSSClass jest pusty, w ogóle nie chcę atrybutu class =.

Oto jeden ze sposobów wykonania warunkowego atrybutu HTML:

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

Czy jest na to bardziej elegancki sposób? W szczególności taki, w którym mógłbym zastosować tę samą składnię, jaka jest używana w innych częściach elementu: class = "@ strCSSClass"?

tony722
źródło

Odpowiedzi:

160

Nie słyszałeś tego ode mnie, kierownika projektu Razor, ale w Razor 2 (strony internetowe 2 i MVC 4) będziemy mieć atrybuty warunkowe wbudowane w Razor (od pomyślnego przetestowania MVC 4 RC), więc możesz po prostu powiedzieć takie rzeczy ...

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

Jeśli strCSSClass ma wartość null, atrybut klasy w ogóle nie będzie renderowany.

SSSHHH ... nie mów. :)

Erik Porter
źródło
1
Tak, zdecydowanie nie powinieneś akceptować mojej jako odpowiedzi. To powiedziawszy, dzisiaj nie ma czystszego sposobu, aby to zrobić (dlatego tworzymy czystszy sposób, aby to zrobić). Prawdopodobnie najczystszym sposobem na to jest użycie Html.TextBox, ale ma to inny zestaw mniej pożądanych rzeczy. :( Cieszę się, że podoba Ci się to, co dodajemy. :)
Erik Porter
1
Ale jak mogę połączyć atrybuty Razor z innym tekstem? Muszę wprowadzić następujące dane: ... id = "[email protected]". Spodziewałem się czegoś takiego jak ... id = "track_2", ale wygenerowało to następujące dane wyjściowe: ... id = "[email protected]" ...
Laserson
2
Możesz zmusić Razor do oceny kodu, umieszczając wokół niego parens. id = "track _ @ (track.ID)"
Erik Porter
1
Dodano uwagę wskazującą, że działa to pomyślnie z MVC 4. Jeśli korzystasz z MVC 3, zobacz moją odpowiedź poniżej.
AaronLS
4
@ErikPorter PROSZĘ NIE WIADOMIĆ Z MOIM HTML! zobacz także stackoverflow.com/questions/9234467/ ... to również dla innych złych zachowań
eaglestorm
126

Zauważ, że możesz zrobić coś takiego (przynajmniej w MVC3):

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

Uważałem, że dodawanie cytatów za pomocą brzytwy było w rzeczywistości przeglądarką. Jak zauważył Rism podczas testowania z MVC 4 (nie testowałem z MVC 3, ale zakładam, że zachowanie się nie zmieniło), to faktycznie daje, class=TopBorderale przeglądarki są w stanie przeanalizować to dobrze. Parsery HTML nieco wybaczają brakujące cudzysłowy atrybutów, ale może się to zepsuć, jeśli masz spacje lub określone znaki .

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

LUB

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

Co idzie nie tak z podawaniem własnych ofert

Jeśli spróbujesz użyć niektórych zwykłych konwencji C # dla zagnieżdżonych cudzysłowów, otrzymasz więcej cudzysłowów niż się spodziewasz, ponieważ Razor próbuje bezpiecznie uciec od nich. Na przykład:

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

To powinno oceniać się <button type="button" style="border:0px">jednak Razor ucieka wszystkie wyjścia z C # i tym samym produkuje:

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

Zobaczysz to tylko wtedy, gdy przeglądasz odpowiedź przez sieć. Jeśli używasz inspektora HTML, często faktycznie widzisz DOM, a nie surowy HTML. Przeglądarki parsują HTML do DOM, a reprezentacja DOM po przeanalizowaniu ma już zastosowane pewne elementy. W tym przypadku przeglądarka widzi, że nie ma cudzysłowów wokół wartości atrybutu, dodaje je:

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

Ale w Inspektorze DOM kody znaków HTML są wyświetlane poprawnie, więc faktycznie widzisz:

style=""border:0px""

W przeglądarce Chrome, jeśli klikniesz prawym przyciskiem myszy i wybierzesz Edytuj HTML, przełączy się z powrotem, abyś mógł zobaczyć te paskudne kody znaków HTML, wyjaśniając, że masz prawdziwe cudzysłowy zewnętrzne i wewnętrzne cudzysłowy zakodowane w HTML.

Więc problem z próbą wykonania cytowania jest taki, że Razor wymyka się tym.

Jeśli chcesz mieć pełną kontrolę nad ofertami

Użyj Html.Raw, aby zapobiec ucieczce cytatów:

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

Renderuje jako:

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

Powyższe jest całkowicie bezpieczne, ponieważ nie wyświetlam kodu HTML ze zmiennej. Jedyną zaangażowaną zmienną jest warunek trójskładnikowy. Uważaj jednak, ponieważ ta ostatnia technika może narazić Cię na pewne problemy z bezpieczeństwem, jeśli budujesz ciągi znaków z danych dostarczonych przez użytkownika. Np. Jeśli zbudowałeś atrybut z pól danych, które pochodzą z danych dostarczonych przez użytkownika, użycie Html.Raw oznacza, że ​​ciąg może zawierać przedwczesne zakończenie atrybutu i tagu, a następnie rozpocznij tag skryptu, który robi coś w imieniu aktualnie zalogowanego użytkownik (prawdopodobnie inny niż zalogowany użytkownik). Może masz stronę z listą wszystkich zdjęć użytkowników i ustawiasz podpowiedź tak, aby była nazwą użytkownika każdej osoby, a jeden użytkownik nazwał siebie'/><script>$.post('changepassword.php?password=123')</script> a teraz każdy inny użytkownik przeglądający tę stronę ma natychmiastowo zmienione hasło na hasło znane złośliwemu użytkownikowi.

AaronLS
źródło
To bardzo dobra uwaga! W rzeczywistości sprawia, że ​​jest czytelny i użyteczny w większości sytuacji.
Dmytro Szewczenko
To właśnie robiłem w przykładzie w moim pytaniu. Dzięki za bardziej szczegółowe wyjaśnienie i przykład. :)
tony722
1
Uważaj jednak na przestrzenie. "style = display: none;" renderuje się jako brak; = "": = "" style = "display"
wmcainsh
1
Dlaczego więc to nie działa re: magic podwójne cudzysłowy <span @ (true? "Class = logo-owner": string.Empty) /> Po prostu tworzy <span class = logo-owner />
rism
1
@AaronLS Tak, dokładnie tak jest. Nie mogę zrozumieć, w jaki sposób przeglądarka (Chrome 40 / FF33.1 / IE 10) wpłynęłaby na cokolwiek, ponieważ jest to znacznik wygenerowany przez serwer, a jeśli tak, to dlaczego tylko te dwa atrybuty klasy, ale nie atrybut klasy przycisku zapytania, a nawet type = Atrybuty „przycisku” wszystkich trzech przycisków. Zdecydowanie jest to serwer IMHO, ponieważ mogę również RDP na kilku maszynach wirtualnych Azure rozsianych po całym świecie, aby uzyskać ten sam wynik IE / Firefox / Chrome.
ryzm
12

Wydaje mi się, że trochę wygodniejszym i bardziej uporządkowanym sposobem jest użycie helpera HTML. Twoim zdaniem może to wyglądać tak:

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

@* ... *@

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

Jeśli ten sposób będzie dla Ciebie przydatny, polecam zdefiniowanie słownika htmlAttrw modelu, aby Twój widok nie wymagał żadnych @{ }bloków logicznych (aby był bardziej przejrzysty).

Yaschur
źródło
4
-1, nigdy nie polecaj nikomu umieszczania logiki w widokach. Widoki powinny być odpowiedzialne tylko za renderowanie. Zamiast tego dodaj przykład HtmlHelper, a dam Ci +1.
jgauffin
1
Aby przetestować rzeczy, mogę użyć bloku kodu w widoku - jest to szybsze. I poleciłem przenieść ten blok do modelu.
Yaschur
3
tak, ale odpowiedzi powinny pokazywać najlepsze praktyki, a nie szybką i brudną wersję, która sprawia, że ​​utrzymanie kodu jest koszmarem.
jgauffin
@jgauffin Myślę, że pomocnik HTML jest tutaj niepotrzebny. zobacz moją odpowiedź.
gdoron wspiera Monikę w
+1 @Yaschur Twoja odpowiedź jest inspirująca. Tak trzymaj. to znaczy. dzięki funkcji konwersji jawnej w języku C # możemy jeszcze bardziej ulepszyć tę odpowiedź. Nie cały kod trzeba było ukształtować w określony sposób. Zawsze jest lepszy sposób, którego nie jesteśmy świadomi. Niektóre projekty opowiadają się za organizacją kodu. Organizacja kodu jest z praktyki, a nie koncepcjami!
Bamboo