Blazor - 组件包装和数据绑定

Posted

技术标签:

【中文标题】Blazor - 组件包装和数据绑定【英文标题】:Blazor - Component wrapping and data binding 【发布时间】:2020-12-19 14:47:01 【问题描述】:

我在 blazor 组件中进行了范围输入。 现在我正在尝试进行对数范围输入。

为此,我想使用我的第一个范围输入,并简单地在范围输入中的假值和真实值之间进行包装。

但是我觉得我对组件绑定的理解还不够,索引页面没有被通知修改。

这是范围输入组件:

<div id="rc-@ID">
    @(new MarkupString($@"<style>
                #rc-ID 
                    position: relative;
                    width: 100%;
                

                #rc-ID > input[type='range'] 
                    padding: 0;
                    margin: 0;
                    display: inline-block;
                    vertical-align: top;
                    width: 100%;
                    --range-color: hsl(211deg 100% 50%);
                    background: var(--track-background);
                
                #rc-ID > input[type='range']::-moz-range-track 
                    border-color: transparent; /* needed to switch FF to 'styleable' control */
                
                #rc-ID > input[name='low-range'] 
                    position: absolute;
                
                #rc-ID > input[name='low-range']::-webkit-slider-thumb 
                    position: relative;
                    z-index: 2;
                
                #rc-ID > input[name='low-range']::-moz-range-thumb 
                        transform: scale(1); /* FF doesn't apply position it seems */
                        z-index: 1;
                    
                #rc-ID > input[name='high-range'] 
                    position: relative;
                    --track-background: linear-gradient(to right, transparent (int)(100 * (LowerValue - MinBound) / (MaxBound - MinBound) + 1 + 0.5f)%, var(--range-color) 0, var(--range-color) (int)(100 * (HigherValue - MinBound) / (MaxBound - MinBound) - 1 + 0.5f)%, transparent 0 ) no-repeat 0 50% / 100% 100%;
                    background: linear-gradient(to right, gray (int)(100 * (LowerValue - MinBound) / (MaxBound - MinBound) + 1+ 0.5f)%, transparent 0, transparent (int)(100 * (HigherValue - MinBound) / (MaxBound - MinBound) - 1 + 0.5f)%, gray 0 ) no-repeat 0 50% / 100% 30%
                

                #rc-ID > input[type='range']::-webkit-slider-runnable-track 
                    background: var(--track-background);
                

                #rc-ID > input[type='range']::-moz-range-track 
                    background: var(--track-background);
                
            </style>"))
    <input class="custom-range" name="low-range" type="range" min="@MinBound" max="@MaxBound" step="@Step" @bind="@LowerValue" @bind:event="oninput" />
    <input class="custom-range" name="high-range" type="range" min="@MinBound" max="@MaxBound" step="@Step" @bind="@HigherValue" @bind:event="oninput" />
</div>

@code

    [Parameter] public float MinBound  get; set;  = 0;
    [Parameter] public float MaxBound  get; set;  = 1;
    [Parameter] public float Step  get; set;  = 0.01f;
    [Parameter]
    public float? ValueLow
    
        get
        
            var res = Math.Min(_valueLow, _valueHigh);
            if (res == MinBound)
                return null;
            return res;
        
        set
        
            if (!value.HasValue)
            
                if (_valueLow.Equals(MinBound))
                    return;
                _valueLow = MinBound;
            
            else
            
                if (_valueLow.Equals(value.Value))
                    return;
                _valueLow = value.Value;
            

            if (_valueLow > _valueHigh)
            
                _valueLow = _valueHigh;
                _valueHigh = value.Value;
                ValueHighChanged.InvokeAsync(_valueHigh);
            

            if (_valueLow == MinBound)
                ValueLowChanged.InvokeAsync(null);
            else
                ValueLowChanged.InvokeAsync(_valueLow);
        
    
    [Parameter]
    public float? ValueHigh
    
        get
        
            var res = Math.Max(_valueLow, _valueHigh);
            if (res == MaxBound)
                return null;
            return res;
        
        set
        
            if (!value.HasValue)
            
                if (_valueHigh.Equals(MaxBound))
                    return;
                _valueHigh = MaxBound;
            
            else
            
                if (_valueHigh.Equals(value.Value))
                    return;

                _valueHigh = value.Value;
            
            if (_valueLow > _valueHigh)
            
                _valueHigh = _valueLow;
                _valueLow = value.Value;
                ValueLowChanged.InvokeAsync(_valueLow);
            

            if (_valueHigh == MaxBound)
                ValueHighChanged.InvokeAsync(null);
            else
                ValueHighChanged.InvokeAsync(_valueHigh);
        
    


    [Parameter] public EventCallback<float?> ValueLowChanged  get; set; 
    [Parameter] public EventCallback<float?> ValueHighChanged  get; set; 

    float _valueLow = 0;
    float _valueHigh = 1;
    private float LowerValue
    
        get => Math.Min(_valueLow, _valueHigh);
        set => ValueLow = value;
    
    private float HigherValue
    
        get => Math.Max(_valueLow, _valueHigh);
        set => ValueHigh = value;
    


    string ID = Guid.NewGuid().ToString().Replace("-", "").Substring(15);

这是我的 Range 输入日志组件:

<RangeControl @bind-ValueLow="Low"
              @bind-ValueHigh="High"
              MaxBound="max"
              MinBound="min"
              Step="1" />
<div class="d-flex">
    <strong>Log values : </strong>
    <span>@Low</span>
    <span class="ml-2">@High</span>
</div>

@code

    private float min = 1.0f;
    private float max = 100.0f;

    [Parameter] public float MinBound  get; set;  = 10;
    [Parameter] public float MaxBound  get; set;  = 10000;
    [Parameter] public float Step  get; set;  = 1;


    private float r => MinBound == 0 ? MaxBound : (MaxBound / MinBound);

    private float? _valueLow;
    [Parameter]
    public float? ValueLow
    
        get => _valueLow;
        set
        
            if (value == _valueLow) return;
            _valueLow = value;
            ValueLowChanged.InvokeAsync(ValueLow);
        
    

    private float? _valueHigh;
    [Parameter]
    public float? ValueHigh
    
        get => _valueHigh;
        set
        
            if (value == _valueHigh) return;
            _valueHigh = value;
            ValueHighChanged.InvokeAsync(ValueHigh);
        
    



    private float? Low
    
        get
        
            if (ValueLow.HasValue)
                return (float)((min = max) * Math.Log(ValueLow.Value) / Math.Log(r));
            return null;
        
        set
        
            if (value.HasValue)
                ValueLow = (float)Math.Exp(value.Value * Math.Log(r) / (max - min));
            else
                ValueLow = null;
        
    
    private float? High
    
        get
        
            if (ValueHigh.HasValue)
                return (float)((min = max) * Math.Log(ValueHigh.Value) / Math.Log(r));
            return null;
        
        set
        
            if (value.HasValue)
                ValueHigh = (float)Math.Exp(value.Value * Math.Log(r) / (max - min));
            else
                ValueHigh = null;
        
    

    [Parameter] public EventCallback<float?> ValueLowChanged  get; set; 
    [Parameter] public EventCallback<float?> ValueHighChanged  get; set; 

还有索引页:

@page "/"

<h1>Hello, world!</h1>

<RangeControl @bind-ValueHigh="ValueHigh" @bind-ValueLow="ValueLow" MinBound="10" MaxBound="10000" Step="1"></RangeControl>
<br />
<RangeControlLog @bind-ValueHigh="ValueHigh" @bind-ValueLow="ValueLow" MinBound="10" MaxBound="10000" Step="1"></RangeControlLog>

<div class="d-flex">
    <strong>Real values : </strong>
    <span>@ValueLow</span>
    <span class="ml-2">@ValueHigh</span>
</div>

@code 
    float? ValueHigh = null;
    float? ValueLow = null;

【问题讨论】:

【参考方案1】:

您不能嵌套@bind-,即拥有一个使用被包装组件的@bind- 的包装器,并且还公开一个与@bind- 一起使用的属性。

您需要将FooFooChanged 传递给被包装的组件。

这意味着在您的RangeControlLog 中,您需要传递给RangeControl ValueLowValueLowChanged 而不是使用@bind-ValueLow

<RangeControl ValueLow="Low"
              ValueHigh="High"
              ValueLowChanged="ValueLowChanged"
              ValueHighChanged="ValueHighChanged"
              MaxBound="max"
              MinBound="min"
              Step="1" />

要了解更多信息,您可以查看有关 chained binding 的文档,也可以查看我制作的 this question,以更好地了解 ValueChanged 及其工作原理。

但简而言之,当您使用@bind-Foo="Bar" 时,它会将其转换为Foo="Bar"FooChanged="@(foo =&gt; Bar = foo;)",它们是用于更新属性的一种默认值。但是当你有多个@bind-时它不起作用,所以你需要直接传递它。

对我来说,@bind- 看起来像是绑定属性的语法糖,当你有参数FooFooChanged 时,你可以使用@bind-Foo

【讨论】:

以上是关于Blazor - 组件包装和数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

如何让两种方式的数据绑定在 blazor 中工作?

Blazor 数据绑定开发指南

组件内的 Blazor 双向绑定文本区域

Blazor University 组件 — 双向绑定

Blazor University 组件 — 单向绑定

Blazor数据绑定