Existe-t-il un moyen d'ajouter un rappel à Javascript et d'obtenir le résultat dans Blazor? En dehors des promesses de JS.
par exemple, disons que je veux charger un fichier
Code Javascript
// read with javascript void ReadFileContent() { JsRuntime.InvokeAsync<object>("readFile", "file.txt", "resultCallbackMethod"); } // output result callback to console void resultCallbackMethod(string text) { Console.Write(text); }
Puis-je avoir quelque chose comme ça dans Blazor C #
// read file content and output result to console void GetFileContent() { JsRuntime.InvokeAsync<object>("readFile", "file.txt", (string text) => { Console.Write(text); }); }
Ou peut-être quelque chose comme ça
window.readFile = function(filePath, callBack) { var reader = new FileReader(); reader.onload = function (evt) { callBack(evt.target.result); }; reader.readAsText(filePath); }
Merci
4 Réponses :
Je crois que vous recherchez les informations sur la documentation ici: https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interop?view=aspnetcore-3.0#invoke-net-methods- from-javascript-functions
Il montre comment appeler le Razor.Net à partir de Javascript. La documentation contient plus d'informations, mais vous aurez essentiellement besoin de l'attribut [JSInvokable] sur la méthode dans razor et de l'appel via DotNet.invokeMethod en javascript.
Merci, mais pas vraiment, juste un rappel direct. Quelque chose comme passer une fermeture de rappel de résultat à JsRuntime.InvokeAsync directement.
MISE À JOUR 1:
Après avoir relu votre question, je pense que cela couvrirait votre deuxième exemple
Je pense que vous avez la possibilité d'implémenter une fonction proxy JS qui gère l'appel. Quelque chose comme ça:
MISE À JOUR 2:
Le code a été mis à jour avec une version fonctionnelle (mais pas profondément testée), vous pouvez également trouver un exemple fonctionnel sur blazorfiddle.com
CODE JAVASCRIPT
@page "/" @inject IJSRuntime jsRuntime <div> Select a text file: <input type="file" id="fileInput" @onchange="@ReadFileContent" /> </div> <pre> @fileContent </pre> Welcome to your new app. @code{ private string fileContent { get; set; } public static object CreateDotNetObjectRefSyncObj = new object(); public async Task ReadFileContent(UIChangeEventArgs ea) { // Fire & Forget: ConfigureAwait(false) is telling "I'm not expecting this call to return a thing" await jsRuntime.InvokeAsync<object>("readFileProxy", CreateDotNetObjectRef(this), "ReadFileCallback", ea.Value.ToString()).ConfigureAwait(false); } [JSInvokable] // This is required in order to JS be able to execute it public void ReadFileCallback(string response) { fileContent = response?.ToString(); StateHasChanged(); } // Hack to fix https://github.com/aspnet/AspNetCore/issues/11159 protected DotNetObjectRef<T> CreateDotNetObjectRef<T>(T value) where T : class { lock (CreateDotNetObjectRefSyncObj) { JSRuntime.SetCurrentJSRuntime(jsRuntime); return DotNetObjectRef.Create(value); } } }
CODE C #
// Target Javascript function window.readFile = function (filePath, callBack) { var fileInput = document.getElementById('fileInput'); var file = fileInput.files[0]; var reader = new FileReader(); reader.onload = function (evt) { callBack(evt.target.result); }; reader.readAsText(file); } // Proxy function // blazorInstance: A reference to the actual C# class instance, required to invoke C# methods inside it // blazorCallbackName: parameter that will get the name of the C# method used as callback window.readFileProxy = (instance, callbackMethod, fileName) => { // Execute function that will do the actual job window.readFile(fileName, result => { // Invoke the C# callback method passing the result as parameter instance.invokeMethodAsync(callbackMethod, result); }); }
Vous avez essayé bro mais cela n'a pas fonctionné, Buh je suis sûr qu'avec peu de modifications, cela devrait. Je cherche toujours quelque chose de plus intelligent. Peut-être avec un délégué ou d'autres moyens.
@ samtax01, j'ai mis à jour ma réponse: le pseudo-code a été supprimé et remplacé par du code de travail, un lien vers un exemple de travail a également été inclus.
Merci pour ça @Henry Rodriguez. J'en ai créé quelque chose et je pensais que cela pourrait aussi être utile.
Notez que DotNetObjectRef.Create (this) fonctionne toujours correctement dans une autre méthode. Il est uniquement noté qu'il y a un problème avec les événements du cycle de vie Blazor dans l'aperçu6. https://github.com/aspnet/AspNetCore/issues/11159 .
Ceci est ma nouvelle implémentation.
// Proxy function that serves as middlemen window.callbackProxy = function(dotNetInstance, callMethod, param, callbackMethod){ // Execute function that will do the actual job window[callMethod](param, function(result){ // Invoke the C# callback method passing the result as parameter return dotNetInstance.invokeMethodAsync(callbackMethod, result); }); return true; }; // Then The Javascript function too window.readFile = function(filePath, callBack) { var reader = new FileReader(); reader.onload = function (evt) { callBack(evt.target.result); }; reader.readAsText(filePath); }
Et dans blazor _Host.cshtml ou index.html, incluez le connecteur de proxy de rappel
<div> Load the file content <button @click="@ReadFileContent">Get File Content</button> </div> <pre> @fileContent </pre> Welcome to your new app. @code{ string fileContent; //The button onclick will call this. void GetFileContent() { JsRuntime.InvokeAsync<object>("callbackProxy", DotNetObjectRef.Create(this), "readFile", "file.txt", "ReadFileCallback"); } //and this is the ReadFileCallback [JSInvokable] // This is required for callable function in JS public void ReadFileCallback(string filedata) { fileContent = filedata; StateHasChanged(); }
Cela fonctionne parfaitement pour ce dont j'avais besoin et il est réutilisable.
En utilisant les astuces de cette page, j'ai proposé une version plus générique qui fonctionne avec presque toutes les fonctions basées sur le rappel.
Vous pouvez maintenant appeler n'importe quelle fonction dont le dernier argument est un rappel. Vous pouvez transmettre n'importe quel nombre d'arguments à la fonction et le rappel peut avoir n'importe quel nombre d'arguments renvoyés.
La fonction InvokeJS renvoie une instance de CallbackerResponse qui peut être utilisée pour obtenir la valeur typée de l'un des arguments de réponse. Voir des exemples et du code pour plus d'informations.
Basé sur le rappel OP (fileContents (string)):
Exemple 1 (C # Blazor avec await):
window._callbacker = function(callbackObjectInstance, callbackMethod, callbackId, cmd, args){ var parts = cmd.split('.'); var targetFunc = window; var parentObject = window; for(var i = 0; i < parts.length; i++){ if (i == 0 && part == 'window') continue; var part = parts[i]; parentObject = targetFunc; targetFunc = targetFunc[part]; } args = JSON.parse(args); args.push(function(e, d){ var args = []; for(var i in arguments) args.push(JSON.stringify(arguments[i])); callbackObjectInstance.invokeMethodAsync(callbackMethod, callbackId, args); }); targetFunc.apply(parentObject, args); };
Exemple 2 (C # Blazor avec rappel):
using Microsoft.JSInterop; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Home.Services { public class CallbackerResponse { public string[] arguments { get; private set; } public CallbackerResponse(string[] arguments) { this.arguments = arguments; } public T GetArg<T>(int i) { return JsonConvert.DeserializeObject<T>(arguments[i]); } } public class Callbacker { private IJSRuntime _js = null; private DotNetObjectReference<Callbacker> _this = null; private Dictionary<string, Action<string[]>> _callbacks = new Dictionary<string, Action<string[]>>(); public Callbacker(IJSRuntime JSRuntime) { _js = JSRuntime; _this = DotNetObjectReference.Create(this); } [JSInvokable] public void _Callback(string callbackId, string[] arguments) { if (_callbacks.TryGetValue(callbackId, out Action<string[]> callback)) { _callbacks.Remove(callbackId); callback(arguments); } } public Task<CallbackerResponse> InvokeJS(string cmd, params object[] args) { var t = new TaskCompletionSource<CallbackerResponse>(); _InvokeJS((string[] arguments) => { t.TrySetResult(new CallbackerResponse(arguments)); }, cmd, args); return t.Task; } public void InvokeJS(Action<CallbackerResponse> callback, string cmd, params object[] args) { _InvokeJS((string[] arguments) => { callback(new CallbackerResponse(arguments)); }, cmd, args); } private void _InvokeJS(Action<string[]> callback, string cmd, object[] args) { string callbackId; do { callbackId = Guid.NewGuid().ToString(); } while (_callbacks.ContainsKey(callbackId)); _callbacks[callbackId] = callback; _js.InvokeVoidAsync("window._callbacker", _this, "_Callback", callbackId, cmd, JsonConvert.SerializeObject(args)); } } }
Basé sur un rappel commun (erreur (chaîne), données (objet)):
Exemple 3 (C # Blazor avec await):
[Inject] public Callbacker Callbacker { get; set; }
Dans votre Blazor Program.cs Main, ajoutez un singleton (ou une portée si vous le souhaitez) Callbacker
builder.Services.AddSingleton<Services.Callbacker>();
Ajoutez le service Callbacker dans votre page Blazor. Exemple: MyPage.razor.cs
// To call a javascript function with the arguments (arg1, arg2, arg3, callback) // and where the callback arguments are (err, data) var response = await Callbacker.InvokeJS("window.myObject.myFunction", arg1, arg2, arg3); // deserialize callback argument 0 into C# string var err = response.GetArg<string>(0); // deserialize callback argument 1 into C# object var data = response.GetArg<MyObjectType>(1);
C #
Callbacker.InvokeJS((response) => { var fileContents = response.GetArg<string>(0); // fileContents available here }, "window.readFile", filename);
JS
var response = await Callbacker.InvokeJS("window.readFile", filename); var fileContents = response.GetArg<string>(0); // fileContents available here
Je ne sais pas pour le rappel, mais ne pouvez-vous pas utiliser une promesse à la place?
je peux, mais j'ai ce groupe de codes avec rappel uniquement.