Install dotnet

You will need .NET SDK installed.

Follow the instructions for your platform at dotnet.microsoft.com/learn/aspnet/blazor-tutorial/install.

To verify that it's working, simply type dotnet in your console.

Create a Blazor App

This will create a new basic Blazor app in a new folder. If you already have an app you can simply skip this step.

dotnet new blazorserver -o BlazorApp --no-https

cd BlazorApp

dotnet watch run

This will build and launch the dev server, usually on http://localhost:5000

Add the Weavy script

The simplest way of adding the Weavy script is to add it to the <head> section in your Pages/_Host.cshtml. Replace {your-server-domain} with the domain name of your own Weavy server.

Pages/_Host.cshtml

<head>

    ...

    <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

    <script src="https://{your-server-domain}/javascript/weavy.js"></script>

</head>

Create a JS Interop

To be able to work with Weavy in Javascript we need a JS Interop in Blazor. The JS interop acts as a bridge between Blazor and JS. To not expose our bridge in the window object, we will create the bridge as a JS module that we will import.

Create wwwroot/weavyJsInterop.js. We just have to expose the creation of a Weavy instance. Everything after that will be accessible as a IJSObjectReference on which we may call methods and use properties.

weavyJsInterop.js

export function weavy(...options) {

    return new window.Weavy(...options);

}

Adding a JS Interop Service

We will create a service wrapped around the JS Interop and the Weavy instance. This will provide a much simpler C# syntax around Weavy, much like the Weavy syntax used in Javascript.

Create a folder Weavy for our classes.

Place a C# class called WeavyJsInterop.cs in the Weavy folder. In this file, we will first import our JS Interop Module. We can then create a new Weavy instance using the JS Module and access all methods and properties of that instance.

We will delay the initialization of the module until we request the Weavy Instance to only use it when needed.

WeavyJsInterop.cs

using System;

using System.Threading.Tasks;

using Microsoft.JSInterop;

namespace BlazorApp.Weavy {

    public class WeavyJsInterop : IDisposable {

        private readonly IJSRuntime JS;

        private bool Initialized = false;

        private IJSObjectReference Bridge;

        private ValueTask<IJSObjectReference> WhenImport;

        // Constructor

        // This is a good place to inject any authentication service you may use to provide JWT tokens.

        public WeavyJsInterop(IJSRuntime js){

            JS = js;

        }

        // Initialization of the JS Interop Module

        // The initialization is only done once even if you call it multiple times

        public async Task Init() {

            if (!Initialized) {

                Initialized = true;

                WhenImport = JS.InvokeAsync<IJSObjectReference>("import", "./weavyJsInterop.js");

                Bridge = await WhenImport;

            } else {

                await WhenImport;

            }

        }

        // Calling Javascript to create a new instance of Weavy via the JS Interop Module

        public async ValueTask<IJSObjectReference> Weavy(object options = null) {

            await Init();

            // Place your JWT token here

            var jwt = new { jwt = "{your-generated-jwt-token}" };

            return await Bridge.InvokeAsync<IJSObjectReference>("weavy", new object[] { jwt, options });

        }

        public void Dispose() {

            Bridge?.DisposeAsync();

        }

    }

}

Remember to configure your JWT.

Register the JS Interop Service

To be able to use the JS Interop as a service throughout the app, we need to register it in our Startup.cs. Add the following line in the ConfigureServices method. You also need to use the namespace.

Startup.cs

using BlazorApp.Weavy;

...

public void ConfigureServices(IServiceCollection services) {

    ...

    services.AddScoped<WeavyJsInterop>();

}

Adding IJSObjectReference wrappers

When communicating with Javascript from your app, you'll get back references to the Javascript objects. For convenience, we will wrap Weavy, spaces, and apps in extended IJSObjectReference classes. This is a convenient way to expose Javascript methods and properties to C#. It's also convenient to manage the Javascript cleanup here when you want to dispose of them.

First off, we'll make a base class for IJSObjectReference, so we can extend it easily. Create a class file ExtendableJSObjectREference.cs in your Weavy folder

Weavy/ExtendableJSObjectReference.cs

using Microsoft.JSInterop;

using System.Threading;

using System.Threading.Tasks;

namespace BlazorApp.Weavy {

    //

    // Summary:

    // Wrapper around a IJSObjectReference to enable extending

    public class ExtendableJSObjectReference : IJSObjectReference {

        public IJSObjectReference ObjectReference;

        // Constructed using another IJSObjectReference

        // Possibility to delay ObjectReference assignment

        public ExtendableJSObjectReference(IJSObjectReference objectReference = null) {

            ObjectReference = objectReference;

        }

        // IMPLEMENT DEFAULT

        public ValueTask DisposeAsync() {

            return ObjectReference.DisposeAsync();

        }

        public ValueTask<TValue>

        InvokeAsync<TValue>(string identifier, object[] args) {

            return ObjectReference.InvokeAsync<TValue>(identifier, args);

        }

        public ValueTask<TValue>

        InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object[] args) {

            return ObjectReference.InvokeAsync<TValue>(identifier, cancellationToken, args);

        }

    }

}

Next, we'll create three classes for WeavyReference, SpaceReference, and AppReference.

Create a file WeavyReference.cs in your Weavy folder. The classes will extend the ExtendableJSObjectReference and just add the methods we want to reflect from our Javascript objects. The WeavyReference class also has automatic initialization, so it doesn't need to construct a Weavy instance in Javascript until it's needed.

Weavy/WeavyReference.cs

using Microsoft.JSInterop;

using System.Threading.Tasks;

namespace BlazorApp.Weavy {

    //


    // Summary:


    // Wrapped IJSObjectReference to the Weavy instance in Javascript.


    // Adds .Space() and .Destroy() methods.

    public class WeavyReference : ExtendableJSObjectReference {

        private bool Initialized = false;

        public WeavyJsInterop WeavyService;

        public object Options;

        public ValueTask<IJSObjectReference> WhenWeavy;

        public WeavyReference(WeavyJsInterop weavyService = null, object options = null, IJSObjectReference weavy = null) : base(weavy) {

            Options = options;

            WeavyService = weavyService;

        }

        public async Task Init() {

            if(!Initialized) {

                Initialized = true;

                WhenWeavy = WeavyService.Weavy(Options);

                ObjectReference = await WhenWeavy;

            } else {

                await WhenWeavy;

            }

        }

        public asyncValueTask<SpaceReference> Space(object spaceSelector = null) {

            await Init();

            return new(await ObjectReference.InvokeAsync<IJSObjectReference>("space", new object[] { spaceSelector }));

        }

        // Used for cleanup

        public async Task Destroy() {

            await ObjectReference.InvokeVoidAsync("destroy");

            await DisposeAsync();

        }

    }

    //

    // Summary:

    // Wrapped IJSObjectReference to a Weavy Space in Javascript.

    // Adds .App() and .Remove() methods.

    public class SpaceReference : ExtendableJSObjectReference {

        public SpaceReference(IJSObjectReference space) : base(space) { }

        public async ValueTask<AppReference> App(object appSelector = null) {

            return new(await ObjectReference.InvokeAsync<IJSObjectReference>("app", new object[] { appSelector }));

        }

        // Used for cleanup

        public async Task Remove() {

            await ObjectReference.InvokeVoidAsync("remove");

            await DisposeAsync();

        }

    }

    //

    // Summary:

    // Wrapped IJSObjectReference to a Weavy App in Javascript.

    // Adds .Open(), .Close(), .Toggle() and .Remove() methods()

    public class AppReference : ExtendableJSObjectReference {

        public AppReference(IJSObjectReference app) : base(app) { }

        public ValueTask Open() {

            return ObjectReference.InvokeVoidAsync("open");

        }

        public ValueTask Close() {

            return ObjectReference.InvokeVoidAsync("close");

        }

        public ValueTask Toggle() {

            return ObjectReference.InvokeVoidAsync("toggle");

        }

        // Used for cleanup

        public async Task Remove() {

            await ObjectReference.InvokeVoidAsync("remove");

            await DisposeAsync();

        }

    }

}

Now we have a nice toolbox for being able to use Weavy in components or for custom code! Just add the namespace to your _Imports.razor file to be able to use it anywhere.

_Imports.razor

...

@using BlazorApp.Weavy

Creating a Weavy instance component

First, we will create a component for the Weavy instance. This will be a component creating a Weavy scope for its children.

First, we inject the WeavyJsInterop service into the component. We just make use of the service when creating a new WeavyReference. We map all component parameters to Weavy options using attribute splatting.

We make use of the CascadingValue tag to provide the WeavyReference to any children. This means we can make use of the same Weavy instance for multiple Weavy apps that are placed as children of this component. 

At last, we also let the Dispose method call the .destroy() method on our WeavyReference to clean up in Javascript whenever the component gets disposed of.

Create a Weavy.razor component in your Shared folder. 

Shared/Weavy.razor

@implements IDisposable

@inject WeavyJsInterop WeavyService

<CascadingValue Value="WeavyRef">

    @ChildContent

</CascadingValue>

@code{

    WeavyReference WeavyRef

    [Parameter]

    public RenderFragment ChildContent { get; set; }

    [Parameter(CaptureUnmatchedValues = true)]

    public IDictionary<string, object> Options { get; set; }

    protected override void OnInitialized() {

        WeavyRef = new(WeavyService, Options);

    }

    public void Dispose() {

        WeavyRef?.Destroy();

    }

}

This component may now be used both with or without attributes.

<Weavy>

    <!-- Any children here will have access to the weavy instance using a cascading parameter -->

    ...

</Weavy>

<Weavy id="weavy-blazor" plugins="@(new { deeplinks = true })">

    <!-- Another Weavy instance with some custom options passed to javascript -->

    ...

</Weavy>

Creating the Weavy Chat component

The Weavy Chat component will make use of the Weavy instance and must therefore be placed as a child of the Weavy Instance component. The Weavy instance is caught by the CascadingParameter, which will match the type of the CascadingValue.

A div with a @ref is used to pass as a container reference in options when creating the app in Javascript.

We need to create the app in OnAfterRenderAsync to be able to use the ElementReference of the div.

Create a parameter for the space key. If you want more flexibility in more advanced scenarios, you can create a separate Weavy space component as a layer in between the Weavy component and the Weavy app component. Most of the time it will do to just have the space key as a parameter on the Weavy app.

Shared/WeavyApp.razor

@implements IDisposable

<div @ref="WeavyContainer" class="weavy-app"></div>

@code{

    ElementReference WeavyContainer;

    [CascadingParameter]

    protected WeavyReference Weavy { get; set; }

    [Parameter]

    public string SpaceKey { get; set; }

    [Parameter(CaptureUnmatchedValues = true)]

    public IDictionary<string, object> Options { get; set; }

    public SpaceReference Space;

    public AppReference App;

    protected override async Task OnAfterRenderAsync(bool firstRender) {

        if (firstRender) {

            Options.Add("container", WeavyContainer);

            Space = await Weavy.Space(new { key = SpaceKey });

            App = await Space.App(Options);

        }

    }

    public void Dispose () {

        App?.Remove();

    }

}

Add styling

Add some styling for the container. Weavy always adapts its size to the container where it's placed, therefore we need to specify a height for the container otherwise the height will be 0. You can also make use of display: contents; to make Weavy adapt itself to any parent node of the component instead. This makes it more flexible to use the component in different layouts.

Add a WeavyApp.razor.css file in the Shared folder.

Shared/WeavyApp.razor.css

.weavy-app {

    display: contents;

}

Usage

The Weavy App component is now ready for usage. Weavy requires you to have at least a space key, an app key, and an app type. You must also place the component within a Weavy Instance component and also define a height on the parent.

Let's create a page for Chat.

Pages/Chat.razor

@page "/chat"

<Weavy>

    <div style="height: 100vh;">

        <WeavyApp SpaceKey="blazor-space" type="messenger" @key="ChatKey" key="@ChatKey" name="@ChatName"/>

    </div>

</Weavy>

@code {

    private string ChatKey { get; set; } = "blazor-chat";

    private string ChatName { get; set; } = "Blazor Chat";

}

To make use of Razor expressions for the attributes, you should make use of the @key attribute and set it to the same as the key of the app. This way, the weavy app will get properly replaced when needed.

Don't forget to add the page to your NavMenu.

Shared/NavMenu.razor

...

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">

    <ul class="nav flex-column">

        ...

        <li class="nav-item px-3">

            <NavLink class="nav-link" href="chat">

                <span class="oi oi-comment-square" aria-hidden="true"></span> Chat

            </NavLink>

        </li>

    </ul>

</div>

Now you're all done and dotnet should have automatically have recompiled everything for you! Try it out in the browser!