JSInvokable Blazor method not being called - javascript

I'm trying to invoke a Blazor method in JavaScript inside of an OnSuccess callback for the Plaid API.
Here's the JavaScript that's being run:
async function InitializePlaidLink(objRef, linkToken) {
//console.log("linkToken:" + linkToken);
const handler = Plaid.create({
token: linkToken,
onSuccess: (public_token, metadata) => {
//console.log("public_token: ");
//console.log(public_token);
objRef.invokeMethodAsync('OnPlaidLinkSuccess', public_token);
//console.log("After Invoke Method Async")
},
onLoad: () => {},
onExit: (err, metadata) => {},
onEvent: (eventName, metadata) => {},
//required for OAuth; if not using OAuth, set to null or omit:
//receivedRedirectUri: window.location.href,
});
handler.open();
}
Here's the Blazor code being used:
private string LinkToken { get; set; } = string.Empty;
private string PublicToken { get; set; } = string.Empty;
private async Task InitializePlaid()
{
this.LinkToken = await this.apiService.GetPlaidLinkToken();
var dotNetReference = DotNetObjectReference.Create(this);
await this.jsRuntime.InvokeVoidAsync
(
"InitializePlaidLink",
dotNetReference,
this.LinkToken
);
}
[JSInvokable]
public void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
The Blazor method InitializePlaid is being called to invoke the JS method InitializePlaidLink. Then, on success, the Blazor method OnPlaidLink Success should be called.
I used log statements to confirm that there is a public_token and the JS after the objRef.invokeMethodAsync() is being reached. Also I was able to invoke a Blazor method in a similar way with a different JS method, just not a method with the Plaid API and the onSuccess callback.

The problem is that the OnPlaidLinkSuccess method must be static as follows:
[JSInvokable]
public static void OnPlaidLinkSuccess(string publicToken)
{
this.PublicToken = publicToken;
}
If you have to define a non-static function, then it will be a bit more complicated.
In this case, it is necessary to send a reference of the current component to the JavaScript method. What follows is a solution that you have to adapt yourself with your own codes.
For this reason, I first create this reference using the DotNetObjectReference.Create method and then send it to the JavaScript code using the jSRuntime.InvokeVoidAsync method. In the example below, JsSample is the name of the current component.
Also defined here, onclick refers to a method inside this component.
This reference should also be disposed at the end of the component's work. That's why you see #IDisposable implements.
#page "/js-sample"
#implements IDisposable
#inject IJSRuntime jSRuntime
<button class="btn btn-primary" #onclick="CallInstanceMethod">Invoke Instance Method</button>
#code
{
private DotNetObjectReference<JsSample> objectReference;
[JSInvokable]
public string GetAddress()
{
return "123 Main Street";
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
objectReference = DotNetObjectReference.Create(this);
}
}
private async Task CallInstanceMethod()
{
await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference);
}
public void Dispose()
{
objectReference?.Dispose();
}
}
Now the javascript code that uses this receiving slot will be as follows. In these codes, addressProvider is the received objectReference that can be used to call the component's non-static GetAddress method:
window.JsFunctionHelper = {
invokeDotnetInstanceFunction: function (addressProvider) {
addressProvider.invokeMethodAsync("GetAddress").then((data) => {
console.log(data);
});
}
};

OnPlaidLinkSuccess was being called correctly. The property this.PublicToken was being correctly updated, but the DOM was not being updated for the user to see. Calling this.StateHasChanged() fixed this.

Related

Get the calling object

I am using websocket from node, and I am trying to implement a broadcast method that will send a message to all clients except for the client that sent the message.
To do this, I need to know the
export class WebSocketRoom {
private _clients: WebSocketConnection[] = []
public get clients(): WebSocketConnection[] { return this._clients }
public broadcast(event: string, message: any) {
this.clients.forEach(client => {
client.emit(event, message)
})
return this
}
}
To access the method broadcast, I do this:
class Test extends Module {
public constructor(client: WebSocketConnection) {
super(client)
let room = new WebSocketRoom('hi')
room.broadcast('cool', 'sweet')
}
}
I tried doing a console log of WebSocketRoom.caller but that gives me this error:
TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context.
Is there a way that I can access the object that called broadcast from within the broadcast method without passing it as a parameter?
this.clients.forEach(client => {
if(client == sender) return
client.emit(event, message)
})
Side note
In C#, it is done like so:
public static void MyMethod(this GameObject obj, int var1, int var2) {
obj.add(var1, var2)
}
MyMethod(1,2)
As you can see obj is not passed in when it is called.

Pass a callback from Unity to JavaScript and back to Unity

I am trying to run an Action that sends data to the javascript on the browser, and when the browser finishes it runs a callback, and when the callback completes, it runs the item in the C# which runs the original callback.
Here are is the call order: GameSmart.User.IsGuest(Action origCallback) -> IsGuestUser(/*Executes the javascript*/) -> GuestResponse(string json) -> origCallback()
Once I compile and run the game, I get the following message in the chrome console:
MarshalDirectiveException: Cannot marshal type 'GameSmart.Response`1<GameSmart.IsGuestResponse>'.
I am not sure what that is saying or even means. Is there something I am doing wrong?
This is the class GameSmart.User:
public class User : API {
[DllImport("__Internal")]
public static extern void IsGuestUser(Response<IsGuestResponse> response);
[MonoPInvokeCallback(typeof(Action))]
public static void GuestResponse(Response<IsGuestResponse> r, string data) {
r.callback(JsonUtility.FromJson<IsGuestResponse>(data));
}
public static void IsGuest(Action<IsGuestResponse> callback) {
IsGuestUser(new Response<IsGuestResponse>(callback));
}
}
Here are the Response/Respond classes:
public class Respond { }
public class Response<T> : Respond {
public Action<T> callback;
public Response(Action<T> cb) {
callback = cb;
}
public void Action(Action act, params object[] args) {
act();
}
}
The JavaScript portion looks like this:
var GameSmartJs = {
$GameSmartJs: {},
IsGuestUser: function (obj) {
gamesmart.user.isGuest(function (result) {
this.runCallback('GuestResponse', obj, result);
});
},
runCallback: function (callbackName, callback, result) {
GameSmartJs[callbackName] = callback;
Runtime.dynCall('vZ', callback, Pointer_stringify(result));
}
};
autoAddDeps(GameSmartJs, '$GameSmartJs');
mergeInto(LibraryManager.library, GameSmartJs);
Edit
As per #Programmer suggested, to use _malloc and writeStringToMemory I tried this, and it produces the same error message.
IsGuestUser: function (obj) {
gamesmart.user.isGuest(function (result) {
GameSmartUser.GuestResponse = obj;
var buffer = _malloc(lengthBytesUTF8(result) + 1);
writeStringToMemory(result, buffer);
Runtime.dynCall('vZ', obj, buffer);
});
},
For the next soul that comes across this:
Runtime.dynCall('vZ', obj, buffer);
should be changed to
Runtime.dynCall('vi', obj, [buffer]);

C# Function as parameter - Repeat method structure

I have multiple methods with different signature, and each method has a try-catch block with custom log exception. (Same structure on multiple controllers).
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName1");
}
public static ActionResult Two(Date param3, bool param4)
{
try
{
// Do something
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View("ViwName2");
}
}
I wonder if there's a way to avoid try-catch block for every method and execute another
public class TestController : BaseController
{
public static ActionResult One(int param1, string param2)
{
// Do something (*)
// Call "ActionWithTryCatch" method that has a "function argument" to "Do something (*)"
}
public ActionResult ActionWithTryCatch(MyDelegate del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
¿How can I do that? I've seen examples using delegates but I understand that's strongly typed, so didn't find a way to do that. Thanks!
The pattern that you are describing is close to a form of Aspect Oriented Programming (AOP). However, if you just want to apply specific Try Catch error handling logic to all actions on your controller then it's probably not worth it for you to pull in a whole AOP framework. Instead you could leverage the HandleErrorAttribute or override the OnException method of the controller class.
For example your could write your controller like this:
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
this.service.MethodOne(param1, param2);
return View("ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
this.service.MethodTwo(param3, param4);
return View("ViwName2");
}
protected override void OnException(ExceptionContext filterContext)
{
LogException(filterContext.Exception.Message);
AddModelError(filterContext.Exception.Message);
var errorView = new ViewResult { ViewName = "~/Path/To/Error/View" };
filterContext.Result = errorView;
}
}
If you wanted to abstract it out even more then you could just move the overridden OnException logic into a base controller class and then have all of your controller inherit from the base controller.
If you want to see some additional methods of unified error handling in MVC then check out this blog too: https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
UPDATE
Per my comment, if you insist on implementing the pattern you are describing you could use this modified version of gilmishal's answer.
public class TestController
{
private TestService service;
public TestController(TestService service)
{
this.service = service;
}
public ActionResult One(int param1, string param2)
{
return this.ActionWithTryCatch(() => this.service.MethodOne(param1, param2), "ViwName1");
}
public ActionResult Two(Date param3, bool param4)
{
return this.ActionWithTryCatch(() => this.service.MethodTwo(param3, param4), "ViwName2");
}
public IActionResult ActionWithTryCatch(Action action, string viewName)
{
try
{
action.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
}
return View(viewName);
}
}
this method you created won't work properly if you need to pass parameters to this function in js - so I am assuming you are calling only parameterless methods.
in that case you could use Func<IActionResult> instead of MyDelegate.
public TResult ActionWithTryCatch<TResult>(Func<TResult> del, string viewName)
{
try
{
return del.Invoke();
}
catch (Exception e)
{
LogException(e.Message);
AddModelError(e.Message);
throw;
}
}
this will be more similar to your javascript implementation, and will return a 500 http result upon unhandledexception.
You should call it like this, if you want IActionResult return type -
ActionWithTryCatch<IActionResult>(MethodThatReturnsIActionResult, viewName);
I recommand you look into generics

Pattern for Delegating Calls based on A Given State

I'm trying to think of a pattern I can use.
I want to be able to have a middleman module that takes in a kind of state of a game. Given that state, call a certain method that resides in anotehr module.
What pattern could I use for this?
For example, I want to be able to take in a state of "computer always wins" and based on that state type, I'll call someOtherModule.makeComputerMove(). In the future maybe we want to be able to set the game to a mode where the computer doesn't always win. Well then we could send in a state of "normal game" or something like that which would just call computerAlwaysWins.makeComputerMove() from a different use case module such as normalGame.makeComputerMove()
get the idea?
I can't think of any patterns to provide such a thing...probably because I don't know a lot of them.
You should use State pattern probably with combination of Observer.
public class GameStateContext {
PlayerState Player {get;set; }
// other properties that need to be shared
}
public interface IGameController {
void GoToState(State state)
}
public interface IGameState {
void Start();
void Update();
}
public abstract class GameStateBase : IGameState {
protected GameStateContext _context;
protected IGameController _parent;
public GameStateBase(GameStateContext context, IGameController parent) {
this._context = context;
this._parent = parent;
}
public virtual void Start() {
}
public virtual void Update() {
}
}
public class BonusLevelState : GameStateBase {
public public MainMenuState (GameStateContext context, IGameController parent) : base (context, parent) {
}
public override void Update() {
if(_context.Player.Health == 0) {
_parent.GoToState(GameStates.GameOver);
}
}
}
public GameController : IGameController {
public enum GameStates {
BonusLevel,
InitialState,
....
}
private IGameState currentState;
public GameController() {
// create diferent states
...
currentState = GetState(GameStates.InitialState);
}
public void Update {
currentState.Update();
}
public GoToState(State state) {
currentState = GetState(state);
}
}
I hope you catch an idea, good luck!

Cordova plugin, using javascript to start an android service

I have some issues developing my own plugin. I want my plugin to run as a background process on an android device. Searching the internet, I found using the Service superclass (or a sub-class) being most relevant. Below is some sample code, the execute method in Hello.java is successfully called - the app crashes when I try to start the service (the constructor is successfully instantiated though). Any hints?
//Myservice.java
public class MyService extends IntentService {
public MyService(String name) {
super(name);
Log.d("asd", "constructor");
}
#Override
public void onHandleIntent(Intent intent) {
Log.d("asd", "something");
}
}
//Hello.java
public class Hello extends CordovaPlugin {
#Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) throws JSONException {
Log.d("asd", "we are in execute, hurray!");
Context context = cordova.getActivity().getApplicationContext();
Intent intent = new Intent(context, MyService.class);
MyService service = new MyService("WifiP2pService");
//either this
service.startService(intent);
//or this, tried both
//context.startService(intent);
return true;
}
}
// /plugins/the.package/www/hello.js
module.exports = {
greet: function (name, successCallback, errorCallback) {
cordova.exec(successCallback, errorCallback, "Hello", "greet", [name]);
}
};
// /www/index.js - in onDeviceReady function
var success = function(message) {
alert(message);
}
var failure = function() {
alert("Error calling the plugin");
}
hello.greet("Superdids", success, failure);
I am fairly new to android programming, so any hints would be muchly appreciated!

Categories