How does Blazor call JS methods, C# and JS interoperate

1. Where JavaScript methods can be placed

js methods can be in the following locations:

1.1 in head(not recommended)

<head>
    <script>
      window.jsMethod = (methodParameter) => {
      
      
        ...
      };
    </script>
</head>

Why this is not recommended: If JS depends on Blazor, then interop will fail. And it may also cause the page response to be slowed down.

1.2 bodyin

<body>
    <script src="_framework/blazor.{webassembly|server}.js"></script>
    <script>
      window.jsMethod = (methodParameter) => {
      
      
        ...
      };
    </script>
</body>

1.3 Inject js after Blazor starts

First set blazor.jsthe autostartto false. Then call Blaozr.start().then()the method injection script.

<body>
    <script src="_framework/blazor.{webassembly|server}.js" 
        autostart="false">
        //设置为false
    </script>
    
    <script>
      Blazor.start().then(function () {
      
      
        var customScript = document.createElement('script');
        customScript.setAttribute('src', 'scripts.js');
        document.head.appendChild(customScript);
      });
    </script>
</body>

2. C# method calls JS method

Objects can be used IJSRuntimeto call js methods. There are:

  1. IJSRuntime.InvokeAsync: The first parameter is the method name of js, which is relative to the global ( window), if it is to be called window.some.functionA, the method name is some.functionA. The second parameter is the input parameter of the js method.
  2. JSRuntimeExtensions.InvokeVoidAsync: Use this when you don't need the return value of js.

Example:
JS method:

<script>
  window.convertArray = (win1251Array) => {
    
    
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>

C# method:

@page "/test"
@inject IJSRuntime JS

@code {
    
    
    private async Task ConvertArray()
    {
    
    
        text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
    }
}

3. JS method calls C# method

3.1 Call the static method of C#

You need to use js DotNet.invokeMethodand DotNet.invokeMethodAsyncfunctions. The first parameter is the name of the assembly, the second parameter is the name (or alias) of the static method, and the third and fourth are the input parameters of the static method.

Attributes are used on the C# method being called [JSInvokable].

JS method:

returnArrayAsyncJs: function () {
    
    
  DotNet.invokeMethodAsync('{APP ASSEMBLY}', 'DifferentMethodName','入参1','入参2')
    .then(data => {
    
    
      data.push(4);
      console.log(data);
    });
}

C# method:

@code {
    
    
    [JSInvokable("DifferentMethodName")]
    public static Task<int[]> ReturnArrayAsync(a,b)
    {
    
    
        return Task.FromResult(new int[] {
    
     1, 2, 3 });
    }
}

3.2 Calling the instance method of C#

The following example is from Microsoft documentation, describing how js calls the method HelloHelperof the instance SayHello.
I read it again and felt a little farted: c# first called js, and then this js called hellohelper's sayhell. js can't directly new a hellohelper, so it needs to be used DotNetObjectReference.Create.

  1. Razor page: call interaction class
<button type="button" class="btn btn-primary" @onclick="TriggerNetInstanceMethod">点击</button>

@code {
    
    
    public async Task TriggerNetInstanceMethod()
    {
    
    
    	//调用交互类
        await new ExampleJsInterop(JS).CallHelloHelperSayHello("Blazor");
    }
}
  1. ExampleJsInterop.csInteraction class: calling JS code
public class ExampleJsInterop : IDisposable
{
    
    
    private readonly IJSRuntime js;
    private DotNetObjectReference<HelloHelper> objRef;
    public ExampleJsInterop(IJSRuntime js)
    {
    
    
        this.js = js;
    }
    public ValueTask<string> CallHelloHelperSayHello(string name)
    {
    
    
        objRef = DotNetObjectReference.Create(new HelloHelper(name));
		//调用JS代码
        return js.InvokeAsync<string>(
            "exampleJsFunctions.sayHello",
            objRef);
    }
    public void Dispose()
    {
    
    
        objRef?.Dispose();
    }
}
  1. JS code: Get the example passed from the interactive class HelloHelperand call SayHellothe method
window.exampleJsFunctions = {
    
    
  sayHello: function (dotnetHelper) {
    
    
    return dotnetHelper.invokeMethodAsync('SayHello')
      .then(r => console.log(r));
  }
};
  1. HelloHelperkind:
public class HelloHelper
{
    
    
    public HelloHelper(string name)
    {
    
    
        Name = name;
    }

    public string Name {
    
     get; set; }

    [JSInvokable]
    public string SayHello() => $"Hello, {
      
      Name}!";
}

3.3 Call the instance method of the Razor component

Similar to the call of a static method, the instance method to be called needs to be packaged Action.
JS code, APP ASSEMBLYis the name of the blazor assembly:

function updateMessageCallerJS() {
    
    
  DotNet.invokeMethodAsync('{APP ASSEMBLY}', 'UpdateMessageCaller');
}

Razor components:

@page "/JSInteropComponent"


@code {
    
    
    private static Action action;
    private string message = "Select the button.";
    protected override void OnInitialized()
    {
    
    
        action = UpdateMessage;
    }
    private void UpdateMessage()
    {
    
    
    	//这个是需要调用的实例方法
        message = "UpdateMessage Called!";
        StateHasChanged();
    }
    [JSInvokable]
    public static void UpdateMessageCaller()
    {
    
    
        action.Invoke();
    }
}

3.3.1 A helper class for calling component instance methods

This helper class is useful in the following two situations. If it is not used, it will affect all instances of the same component:

  1. Component is a reusable component similar to Item.
  2. The blazor server application is used, resulting in multiple users using the same component.

The helper class is as follows:

using System;
using Microsoft.JSInterop;

public class MessageUpdateInvokeHelper
{
    
    
    private Action action;
    public MessageUpdateInvokeHelper(Action action)
    {
    
    
        this.action = action;
    }
    [JSInvokable("{APP ASSEMBLY}")]
    public void UpdateMessageCaller()
    {
    
    
        action.Invoke();
    }
}

The component code is as follows:

@inject IJSRuntime JS
<li>
    @message
    <button @onclick="InteropCall" style="display:@display">InteropCall</button>
</li>
@code {
    
    
    private string message = "Select one of these list item buttons.";
    private string display = "inline-block";
    private MessageUpdateInvokeHelper messageUpdateInvokeHelper;

    protected override void OnInitialized()
    {
    
    
        messageUpdateInvokeHelper = new MessageUpdateInvokeHelper(UpdateMessage);
    }
    protected async Task InteropCall()
    {
    
    
        await JS.InvokeVoidAsync("updateMessageCallerJS",
            DotNetObjectReference.Create(messageUpdateInvokeHelper));
    }
    private void UpdateMessage()
    {
    
    
        message = "UpdateMessage Called!";
        display = "none";
        StateHasChanged();
    }
}

(Every time a new component is new, a helper class will also be new)

The JS code is as follows:

window.updateMessageCallerJS = (dotnetHelper) => {
    
    
    dotnetHelper.invokeMethodAsync('{APP ASSEMBLY}', 'UpdateMessageCaller');
    dotnetHelper.dispose();
}

Guess you like

Origin blog.csdn.net/catshitone/article/details/118385306