简体   繁体   中英

C# StateHasChanged() in a razor component does not force <video> tag to load/change the mp4 playing, only using javascript seems to work

I am experimenting with server-side blazor. I am trying to have multiple buttons set/change the .mp4 file playing in a tag in a razor component. The only way I have found to make this work is calling a javascript function via IJSRuntime:InvokeVoidAsync() from the OnParametersSet() in my razor component. The javascript function is located in _Host.cshtml. This seems like a rather ugly solution to what should be a simple problem.

I have tried using StateHasChanged() in the OnClick button functions. The h1-header tag and source src="NewFile" are shown to update when I look at the html render in chrome, and the h1 tag correctly changes on the page when a button is clicked, but the new video is not loaded. My guess is this is tied to the video playing on its own thread, or the video tag itself not changing. I just don't understand how to get this done it from razor/c#.

Because of build errors, the Javascript code was added to File: _Html.cshtml

  <script>
        function loadVideo (strNewVideo)
        {
            document.getElementById('videoSourceId').src = strNewVideo;
            document.getElementById("videoTagId").load();
        }
    </script>

Simple component to play videos... File: VideoPlayer.razor

@inject IJSRuntime theJavaScriptEngine;

<div class="align-content-center">
    <h1>@this.m_strRenderMe</h1>
    <video id="videoTagId" autoplay width="1080" height="720">
        <source id="videoSourceId" src=@this.m_strRenderMe type="video/mp4" />
    </video>
</div>

@code {
    ElementReference theVideoTag;

    [Parameter]
    public string strVideoFilePath { get; set; }

    private string m_strRenderMe;

    protected override void OnParametersSet()
    {
        this.m_strRenderMe = this.strVideoFilePath;

        theJavaScriptEngine.InvokeVoidAsync("loadVideo", this.m_strRenderMe);

        this.StateHasChanged();
    }
}

Razor Page with component from above and 4 buttons isw in File: Counter.razor

@page "/counter"

@using UseBlazorToReadPowerPoint.Classes

@inject CPersistantAppState  thePersistantAppState


<VideoPlayer strVideoFilePath=@thePersistantAppState.m_strVideoPath></VideoPlayer>

<button class="btn btn-primary" @onclick="PlayVideo_1">Video 1</button>
<button class="btn btn-primary" @onclick="PlayVideo_2">Video 2</button>
<button class="btn btn-primary" @onclick="PlayVideo_3">Video 3</button>
<button class="btn btn-primary" @onclick="PlayVideo_4">Video 4</button>

@code {

    void PlayVideo_1()
        {
        thePersistantAppState.m_strVideoPath = "videos/Video-1.mp4";
        }

    void PlayVideo_2()
    {
        thePersistantAppState.m_strVideoPath = "videos/Video-2.mp4";
    }

    void PlayVideo_3()
    {
        thePersistantAppState.m_strVideoPath = "videos/Video-3.mp4";
    }

    void PlayVideo_4()
    {
        thePersistantAppState.m_strVideoPath = "videos/Video-4.mp4";
    }

}

To persist the filename selected. File: CPersistantAppState.cs

namespace UseBlazorToReadPowerPoint.Classes
{
    public class CPersistantAppState
    {
        public string m_strVideoPath;
    }
}

The listed code works. I just cannot figure out how to make this work without the javascript call. Seems like there has to be a cleaner way of doing this.

Issue is not with StateHasChangeg() , it is about html's Video tag :

This method is generally only useful when you've made dynamic changes to the set of sources available for the media element, either by changing the element's src attribute or by adding or removing elements nested within the media element itself. load() will reset the element and rescan the available sources, thereby causing the changes to take effect .

It means that is mandatory invoke load after change src attribute. You can't invoke load from blazor at this time, it means you should to invoke it via IJSRuntime :

Blazor code

<div class="align-content-center">
    <h1>@m_strRenderMe[currentVideo]</h1>
    <button @onclick="ChangeVideo">Change video</button>
    <video id="videoTagId" autoplay width="1080" height="720">
        <source id="videoSourceId" src="@m_strRenderMe[currentVideo]" type="video/mp4" />
    </video>
</div>

@code
{
    int currentVideo = 0;
    string[] m_strRenderMe = new string[] {
        "https://.../videoplayback-1.mp4",
        "https://.../videoplayback-2.mp4"
    };
    protected void ChangeVideo()
    {
        currentVideo = (currentVideo + 1) % 2;
        theJavaScriptEngine.InvokeVoidAsync("loadVideo");
    }
}

JS code

<script>
    function loadVideo ()
    {
        document.getElementById("videoTagId").load();
    }
</script>

Check it out at BlazorFiddle .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM