From this tutorial: How to Integrate YouTube video in Blazor WebAssembly I can pass a YouTube URL to a Blazor component and play the video. However when I want to change the URL to play a different video I cannot see how to change the player's source.
<video id="my-player" controls class="video-js" ></video>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await jsRuntime.InvokeVoidAsync("loadPlayer", "my-player", new {
controls = true,
autoplay = false,
preload = "auto",
width = @Width,
height = @Height,
techOrder = new[] { "youtube" },
sources = new[] {
new { type = "video/youtube", src = @Url}
}
});
}
}
So I went through the tutorial and it's not really designed for creating multiple instances of the video player, especially the js part.
There's probably ways you can use the lifecycle methods with more complex js code to do this. But I think the simplest option is to completely re-render a new component when the Url
changes. We can do that using the @key
blazor attribute. When @key
is used and the value changes then Blazor will treat the component as a new one. Which means it'll go through all the lifecycles.
Here are the steps you can take to make this work:-
First, we add logic for handling disposing of the video player instances, which is important because we'll be creating multiple instances of the video player.
player.js
function loadPlayer(id, options) {
disposePlayer(id);
videojs(id, options);
}
function disposePlayer(id) {
const player = videojs.getPlayer(id);
if (player) {
player.dispose();
}
}
Then, we implement the IAsyncDisposable
interface and use the exposed method DisposeAsync()
to call the newly added disposePlayer
function, so that the Video player instance is correctly disposed.
Player.razor
@inject IJSRuntime jsRuntime
@implements IAsyncDisposable
<h3>Player</h3>
<div class="row align-items-center">
<div class="col-8">
<video id="my-player" controls class="video-js"></video>
</div>
</div>
<h3>@Url</h3>
@code {
[Parameter]
public string Url { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await jsRuntime.InvokeVoidAsync("loadPlayer", "my-player", new
{
controls = true,
autoplay = false,
preload = "auto",
width = 560,
height = 264,
techOrder = new[] { "youtube" },
sources = new[] {
new { type = "video/youtube", src = @Url}
}
});
}
}
public async ValueTask DisposeAsync()
{
await jsRuntime.InvokeVoidAsync("disposePlayer", "my-player");
}
}
Finally, we update the page where the Player.razor
component is being rendered to and add the @key
attribute to it. The component will be re-rendered whenever the value assigned to @key
changes and since we're creating a new video player for each url, it makes sense that the url should be the key.
Note I've added a button and list to toggle between urls for demo purposes.
PlayerPage.razor
@using BlazorVideoJs.Components
@page "/playerpage"
<Player @key="_urls[_idx]" Url="@_urls[_idx]" />
<button class="btn btn-lg btn-primary" @onclick="HandleClick">Change video</button>
@code {
int _idx = 0;
List<string> _urls = new() { "https://www.youtube.com/watch?v=xjS6SftYQaQ", "https://www.youtube.com/watch?v=9dLrtDVDWYg", "https://www.youtube.com/watch?v=l40nk18GUzk" };
void HandleClick()
{
_idx++;
if (_idx == _urls.Count)
{
_idx = 0;
}
}
}