Blazor rerender from static method

526 views Asked by At

I have a blazor server test setup with a leaflet map. To use leaflet I inserted a javascript, which works fine. In the javascript code from the leaflet map I call a C# method :

function onMapClick(e) {
    marker.setLatLng(e.latlng);
    DotNet.invokeMethodAsync('TestProject', 'getCoordinatesFromLeaflet', e.latlng);
}

As far as I understood the C# Method has to be static to receive the values. So my method in C# / Blazor looks like this (works fine too) :

[JSInvokable]
public static void getCoordinatesFromLeaflet(LatLng tmp)
{
    LatLng = tmp;
    //InvokeAsync(StateHasChanged); doesn't work
    //StateHasChanged(); doesn't work
}

On the FrontEnd / HTML I show the current coordinates:

<div>
Latitude: @LatLng.Lat.ToString("0.00000") , Longitude : @LatLng.Lng.ToString("0.00000") <div/>

But the coordinates aren't updated on the FrontEnd. I have to rerender manually to let them show the latest position. In other non-static functions I used "InvokeAsync(StateHasChanged);" to solve this problem but here I can't (CS0120).

An object reference is required for the nonstatic field, method, or property 'member'

In order to use a non-static field, method, or property, you must first create an object instance. For more information about static methods, see Static Classes and Static Class Members. For more information about creating instances of classes, see Instance Constructors.

How can I solve this?

1

There are 1 answers

0
chrisbyte On

I know this is old so I cannot take credit for the solution, but I figured out how to do this with an object instance by following a post by Matthew Champion and I wanted to share in case someone else comes across this.

I didn't use a code-behind like Matthew so I did everything in the Razor component, and then there's a simple JavaScript file. Here is a full example of a standard button that calls a JavaScript function, which then calls a .Net function, which then re-renders the component. This should have everything you need to get started.

MyComponent.razor

@implements IDisposable
@inject IJSRuntime JSRuntime

<p>The number is currently set to: @_currentNumber</p>
<button onclick="CallBlazorMethod()">Counter+</button>

@code {
    // Component object reference that javascript uses
    private DotNetObjectReference<MyComponent>? _objRef;
    
    private int _currentNumber;
 
    // These get called FROM javascript
    [JSInvokable]
    public void BlazorMethod()
    {
        _currentNumber++;
        StateHasChanged();  // render
    }

    [JSInvokable]
    public void AnotherBlazorMethod()
    {
        // ...
    }
    
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // tell JavaScript to use our object reference
        _objRef = DotNetObjectReference.Create(this);
        await JSRuntime.InvokeAsync<string>("SetDotNetHelper", _objRef);
        
        if (firstRender)
        {
            // load anything else like normal
        }
    }
    
    public void Dispose()
    {
        _objRef?.Dispose();
    }
}

The JavaScript file has one function that sets the Component object reference (so you don't need to use static), and then whatever functions you want to call from Razor using the reference.

dotnet-helper.js

// set .Net object reference
function SetDotNetHelper(dotNetHelper) {
    window.dotNetHelper = dotNetHelper;
}

// functions your component wants to call

function CallBlazorMethod()
{
    console.log('MyComponent clicked the button');
    
    // call .Net method using the object reference
    window.dotNetHelper.invokeMethodAsync('BlazorMethod');
}

function SomeOtherFunction()
{
    window.dotNetHelper.invokeMethodAsync('AnotherBlazorMethod');
}