JBildstein/SpiderEye

Documentation for invokeApi

Remo opened this issue · 5 comments

Remo commented

I'm trying to figure out the best way to extend the JavaScript functionality within a file executed within spidereyer. I've seen some examples like this:

window._spidereye.invokeApi<DialogResult, MessageBoxConfig>("f0631cfea99a_Dialog.showMessageBox", config, callback);

It seems to related to an annotation BridgeObject, for example this:

[BridgeObject("f0631cfea99a_Dialog")]

What I'm not quite clear about: Why do you prefix the name? Is this just to ensure there are no collisions with other classes?

A simple example in the readme might be cool ;-) Thanks for this cool project, I've been keeping an eye on it for a while and finally started playing around with it, really neat!

The currently best example would be in the Playground.Core project, there's a custom UiBridge class that is used from an Angular client (in app/components/bridge.component.ts). Aside from that, it really needs some proper documentation. I'll write things down here now and expand on it later in the readme (or wiki maybe?)

But first, thank you, I'm glad you like the project :)

As for the prefix, yes, it's to avoid name collisions. You as an end-user of the library don't have to do that since you know all the bridge names. Should you implement a library of bridge objects for others to use, it'd probably be a good idea though. Of course it could be a more friendly prefix.

So lets go through it: on the host side (i.e. in C#) you create a bridge object. That's a completely normal class with methods that are able to be called from the client. Only public methods are callable, they can have one parameter (supplied from the client) and they can return a value (which is sent to the client). Both parameter and return value have to be serializable to JSON (currently using Newtonsoft.Json). Normally the path to call a method would be "BridgeObjectName.methodName" but you can override the bridge name with the BridgeObjectAttribute. Before being able to use it, you have to register it first with either Application.AddGlobalHandler(bridgeObjectInstance) (available for all windows) or on a Window class instance with window.Bridge.AddHandler(bridgeObjectInstance) (only available for this one window).

On the client side there are two ways to do things. Ideally you would use the spidereye npm package and with it the SpiderEye class. Before you call anything on that class, make sure it's ready with the SpiderEye.onReady(yourCallback) event.Then you can use SpiderEye.invokeApi(id, parameter, callback) or var promise = SpiderEye.invokeApiAsync(id, parameter). id is the name of the bridge and method as described above. The callback and promise return an ApiResult object which has a value (any), success (bool) and error (string) field.

The second way would be to use the injected window._spidereye object directly. The invokeApi call works exactly the same way but there is no invokeApiAsync and you have to make sure that window._spidereye is available and ready. I think it's easiest to understand if you check out the implementation of the SpiderEye class (very simple):

export class SpiderEye {

To make things a bit more concrete, here's a bridge object and how you'd call it from the client:

public class SomeBridge
{
    public int IncreaseValue(int value)
    {
        return value + 1;
    }
}

Make sure that you register the bridge object as described above, e.g. Application.AddGlobalHandler(new SomeBridge())

On the client side then:

SpiderEye.onReady(() => {
    // note the camel casing of the method name, increaseValue
    SpiderEye.invokeApi('SomeBridge.increaseValue', 5, result => {
        if (result.success) {
            console.log(result.value); // should display 6
        } else {
            console.error(result.error);
        }
    });
});
Remo commented

Thanks a lot for the thorough answer, I will try to play around a bit and integrate some .NET libraries.

Sure thing, let me know if you run into any problems.

Thanks for the detailed comment above Johannes! I just wanted to note my personal preference in this thread, in case you get to do this:

I'll write things down here now and expand on it later in the readme (or wiki maybe?)

To enable properly versioning the documentation, and thus allow it for consumers of this library to check out the correct docs it's best to just add the docs to the repo in my experience. The wiki seems to enable easier contribution, but in the past I learned that the overhead in keeping the wiki useful due to the issue of not being directly bound to a specific version/commit isn't worth it, in comparison of the smaller onboarding issue that is having to contribute a PR.

Sounds like a new markdown document that gets linked from the readme would do it just fine 😃

thank you @bddckr, that's great input! I didn't think about the versioning aspect of docs. With the docs in the repo that's (almost) automatically there and I can live with the slightly higher onboarding difficulty, it'll be me writing the docs anyway 🙃