如何使用Blazor更改div元素的类

53

我有一个在cshtml页面中的典型div元素,形式如下:

<div id="loginErrors" class="alert alert-danger hide-errors">@(ErrorMessage)</div>

在 Blazor 之前,我通常使用 jQuery 来添加或删除 div 的 hide-errors 类。然而,Blazor 正试图消除对 JavaScript 的需求,我正在尽可能少地使用 JSInterop。在 Blazor 中是否有一种方法可以做到这一点?

因此,在 Blazor 中,我可以这样做:

 @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>
 }

或者使用JSinterop:

调用移除:

await JSRuntime.Current.InvokeAsync<object>("blazorExtensions.RemoveClass", "loginErrors", "hide-errors");

通常情况下,函数会是这样的:

RemoveClass: function (id, classname) {
    var tt = '#' + id;
    $(tt).removeClass(classname);
}

对于添加类名,使用两种方法都可以。但如前所述,我试图避免使用JSInterop,并且我不喜欢声明两次div元素,即使只有一个将进入DOM。

5个回答

57

就像你在常规的Razor中一样:

@if (price>30)
{
    <p>The price is too high.</p>
}

编辑 对于更新后的问题,我猜想您需要这个:

<div class="@((string.IsNullOrEmpty(ErrorMessage)? "hide-errors" : ""))">

我试图避免使用 if else 场景,并声明具有相同 id 的 2 个 div 元素,一个带有 hide-errors 类,另一个没有。因此,我确实想要删除或添加类。 - bilpor
好的。但是你问题中的else有什么作用呢?它并没有添加任何功能。 - Flores
1
你为什么需要一个ID? - Flores
1
我们有ID是因为Specflow需要ID。 - bilpor

22

在 Blazor 中,您可以动态更改 HTML 元素的任何部分,而无需使用 JavaScript。尽管经过多年,我承认我花了一点时间走出旧的 JavaScript 思维模式,但是一旦你做到了,Blazor 真的很棒!

在您的 HTML 中的某个位置,使用一个变量来为任何您想要进行动态样式(或其他)修改的 HTML 元素的类名(或其他属性)。

    <img class="@myImageClass" src="@myImg" />

在 @functions 中声明您创建的任何变量...

    @functions {
    string myImageClass { get; set; }
    string myImg { get; set; } // Swap out the image as well if you want. 

如果你想要最初将项目设置为某些内容,请使用OnInit()。

     protected override void OnInit()
     {
     myImageClass = "myImageVisible";
     myImg = "https://www.somesite.com/ImageToStartWith.png"

     }

在某个函数中,将所需的类更改为您在样式部分中预定义的内容。

    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. 
    }

别忘了事先定义一些你想要使用的样式。

     <style>
     .myImageVisible {
      display: block;
      }

     .myImageHidden{
      display: none;
      }
      </style>

享受。:)


3
对于每个编辑表单中有25个以上字段的LOB应用程序来说,它并不是非常适合,因为您将不得不在视图模型中处理3-4倍数量的属性。想一想。您需要一个属性来存储实际数据,一个属性来存储CSS类,一个属性来存储禁用属性,一个属性来存储tabindex等等。相比之下,使用JS,您只需获取ID并直接设置属性,无需将其保存在广泛的视图模型中。 - Linus Proxy
1
@LinusProxy,这就是Blazor组件的用武之地,你可以使用层次结构来完成其中一些操作。你也可以使用HTML5/CSS3解决一些类似的问题,而不需要使用任何Blazor(C#)或JavaScript。在这个特定的例子中,可以使用CSS3过渡效果(无需JS/C#)。 - Bart Czernicki

3
这是在控制特定元素的UI时我思考过的一个问题(例如突出显示点击的元素),如果有许多这样的元素,比如列表。
两种常见方法(正如其他人所提到的)是:
1. 在元素的class="@someProp some-other-class..."属性中绑定页面/组件字符串属性,该属性可用于设置多个类(因为它只是一个字符串),或添加多个这样的属性(以实现更精细的条件控制)。 2. 创建一个专用的组件,具有将类或CSS值绑定到其中所需的所有属性。
但我想我发现了第三种方法,一种介于两者之间的技术,可以通过对象绑定(甚至是双向绑定)提供非常强大的UI控制,而无需处理单独的组件。因为有时候你需要手动触发父组件StateHasChanged()函数,比如当父子组件之间存在各种依赖关系时,视觉状态机制不会自动刷新。
只需创建一个包装器类,将您要控制的CSS值表示为其属性。可以在同一页/组件的@code部分或某个共享文件夹中进行创建。
例如,我有一个Slide对象列表,由Card对象(并且被视觉化包装)表示,用户可以单击选择和编辑。
项目模型:
public class Slide {
   public int SlideId { get; set; }
   public string Title { get; set; }
   public string Description { get; set; }
   public int SortIndex{ get; set; }
}

项目包装模型:
public class Card {
   Slide Slide { get; set; }
   public string SelectedCssClass { get; set; }
   public string SomeOtherCssClass { get; set; }
   
   public void Reset() {
      SelectedCssClass = "";
   {
}

在组件初始化期间,在 OnIntialized() 方法(如果您获取并等待数据列表,则为 OnInitializedAsync())中,我使用一个新的包装对象来包装每个项目。然后在循环 @foreach() { } 中列出包装的项目时,我将一个 @onclick 事件处理程序附加到每个 div/card 上,传递该卡片对象的引用:

@foreach (Card card in Cards)
{
   <div class="card @card.SelectedCssClass" @onclick="@(e => setSlideToEdit(card))"
}

setSlideToEdit(card) { }函数中,我可以通过简单地调用card.Reset()方法并让绑定完成工作来重置先前选择幻灯片的样式(存储在某些属性中),然后使用新的幻灯片并将card.SelectedCssClass属性设置为所需的样式(或添加另一个专用方法来设置预定义的样式)。

2

对于完全的技术狂热者(比如我),可以创建一个剃刀组件(XAML 爱好者的梦想):

示例用法:

<ContentView Size="Small" IsVisible="@IsOnline">

 <img src="@myImg" />

</ContentView>

结果:应用适当类别的div,class='size-small' 代表 Size="Small"

您可以向此组件添加任何参数,并根据自己的逻辑更改内部类别,保持页面整洁。

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
    }

}

0

我采用了稍微通用一些的版本,参考了@Dima Pavlenko的回答...

我有一个类,它包装了我想要更改类别的对象:

public class HtmlWrapper<T>
{
    public T Item { get; set; }
    public string Class { get; set; }
    public HtmlWrapper(T item, string initialClass) 
    { 
        Item = item; 
        Class = initialClass; 
    }
}

而在我的 Razor 页面中:

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();
    }
}

1
我认为你在第三行缺少一个左括号 [将 ")" 改为 "()" ]... - Naughton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接