Reputation: 3889
I have a typical div element in a cshtml page in the form:
<div id="loginErrors" class="alert alert-danger hide-errors">@(ErrorMessage)</div>
pre Blazor, I'd typically use jQuery to add or remove the hide-errors
class from the div. However, Blazor is trying to remove the need for JavaScript and I'm trying to use as little JSInterop as possible. Is there a way in Blazor to do this?
so in Blazor I could do:
@if (!string.IsNullOrEmpty(ErrorMessage))
{
<div id="loginErrors" class="alert alert-danger">@(ErrorMessage)</div>
}
else
{
<div id="loginErrors" class="alert alert-danger hide-errors">@(ErrorMessage)</div>
}
or using JSinterop :
the Call for removing:
await JSRuntime.Current.InvokeAsync<object>("blazorExtensions.RemoveClass", "loginErrors", "hide-errors");
where the function would typically be :
RemoveClass: function (id, classname) {
var tt = '#' + id;
$(tt).removeClass(classname);
}
with similar for adding a class. Both of the above work, but as mentioned. I'm trying to avoid the JSInterop route and I don't like the div element being declared twice even though only one will get into the DOM.
Upvotes: 59
Views: 85634
Reputation: 8932
Just like you would in regular Razor:
@if (price>30)
{
<p>The price is too high.</p>
}
EDIT For updated question, I guess you want this:
<div class="@(string.IsNullOrEmpty(ErrorMessage)? "hide-errors" : "")">
Upvotes: 68
Reputation: 5476
I've gone for a slightly more generic version of @Dima Pavlenko's answer...
I have a class that wraps the object I want to change the class of:
public class HtmlWrapper<T>
{
public T Item { get; set; }
public string Class { get; set; }
public HtmlWrapper(T item, string initialClass)
{
Item = item;
Class = initialClass;
}
}
And in my razor page:
foreach(var user in UserList)
{
<div class="@user.Class"><a @onclick="() => ClickOnUserAsync(user).ConfigureAwait(false)">@user.Item.Name</a></div>
}
@code {
protected const string Class_User = "user";
protected const string Class_Active = "user active";
private List<HtmlWrapper<UserModel>>? _UserList;
protected List<HtmlWrapper<UserModel>> UserList { get => _UserList ?? new(); set => _UserList = value; }
protected override async Task OnInitializedAsync()
{
if (UserContext.UsersList is not null)
UserList = UserContext.UsersList.Values.OrderBy(o => o.Name).Select(s => new HtmlWrapper<UserModel>(s, Class_User)).ToList();
}
protected async Task ClickOnUserAsync(HtmlWrapper<UserModel> user)
{
user.Class = user.Class == Class_User ? Class_Active : Class_User;
StateHasChanged();
}
}
Upvotes: 0
Reputation: 161
It's a question I asked myself in the context of controlling UI of specific elements (like highlighting a clicked one) in case there are many of them, like lists.
The 2 common ways (as others mentioned) are:
But I think I figured a 3rd way, sort of an "in between" technique, which provides a very powerful UI control with object binding (even a 2-way binding), without dealing with a separate component. Because sometimes you need to trigger the parent component StateHasChanged() manually, like when there are all sorts of dependencies between parent and child components, yet the visual state mechanism doesn't kick in to refresh automatically.
Simply create a wrapper class, representing the item with the CSS values you want to control as its properties. Either on the same page/component @code section or some shared folder.
For example, I had a list of Slide objects, visually represented (and thus wrapped) by a Card object, which the user could click to select and edit.
The item model:
public class Slide {
public int SlideId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int SortIndex{ get; set; }
}
The item wrapper model:
public class Card {
Slide Slide { get; set; }
public string SelectedCssClass { get; set; }
public string SomeOtherCssClass { get; set; }
public void Reset() {
SelectedCssClass = "";
{
}
So during component initialization, in the OnIntialized() method (or OnInitializedAsync() if you fetch and wait for a list of data) I wrap each item with a new wrapper object. Then during @foreach() { } loop, listing the wrapped items, I attach an @onclick event handler to each div/card, passing a reference of that card object:
@foreach (Card card in Cards)
{
<div class="card @card.SelectedCssClass" @onclick="@(e => setSlideToEdit(card))"
}
In setSlideToEdit(card) { } function I can reset the style of previously selected slide (stored in some property) by simply calling the card.Reset() method and let the binding do the work, then use the new one and set the card.SelectedCssClass property to my desire (or add another dedicated method to set a predefined one).
Upvotes: 3
Reputation: 6442
For complete freaks (me) create a razor component (xaml lover dream):
Example usage
<ContentView Size="Small" IsVisible="@IsOnline">
<img src="@myImg" />
</ContentView>
Result: divs with an appropriate class applied, class='size-small'
for Size="Small"
.
You can add whatever parameter to such component and change classes inside upon your logic, keeping your main page clean.
ContentView.razor
@if (IsVisible)
{
<div class="@sizeClass"> @ChildContent</div>
}
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public bool IsVisible { get; set; } = true;
[Parameter]
public string Size {
get
{
return _size.ToString();
}
set
{
_size = Enum.Parse<MySize>(value);
switch (_size)
{
case MySize.Big:
sizeClass = "size-big";
break;
case MySize.Small:
sizeClass = "size-small";
break;
default:
sizeClass = "";
break;
}
}
}
private MySize _size;
private string sizeClass;
public enum MySize
{
Default,
Small,
Big
}
}
Upvotes: 3
Reputation: 281
This is very easy as you can change any part of an html element dynamically in Blazor without using Javascript. After so many years I admit, it took me a little while to get out of the old Javascript mindset, but once you do, Blazor rocks!
In your html somewhere use a variable for the class name (or other attributes) of any html element you want to make dynamic style (or other) modifications to.
<img class="@myImageClass" src="@myImg" />
In @functions declare any variables you created...
@functions {
string myImageClass { get; set; }
string myImg { get; set; } // Swap out the image as well if you want.
if you want to set items to something initially use OnInit()
protected override void OnInit()
{
myImageClass = "myImageVisible";
myImg = "https://www.somesite.com/ImageToStartWith.png"
}
Somewhere in a function change to the desired class to something you pre-defined in the style section.
private async Task DoSomething()
{
myImageClass = "myImageHidden";
myImg = "https://www.somesite.com/NewImageToSwapIn.png"
//Not sure what the point of swapping an image on a hidden element is
//but you get the idea. You can change anything you want anytime.
}
Don't forget to define some styles you want to use beforehand.
<style>
.myImageVisible {
display: block;
}
.myImageHidden{
display: none;
}
</style>
Enjoy. :)
Upvotes: 28