How to...

Overview

The @interopio/ng library provides modules and services for the io.Connect JavaScript libraries - @interopio/browser and @interopio/browser-platform, if you are working on an io.Connect Browser project, or @interopio/desktop, if you are working on an io.Connect Desktop project. The examples below use the @interopio/desktop library. The @interopio/ng library allows you to start using io.Connect features in your Angular apps idiomatically in the context of the Angular framework.

Prerequisites

This package should be used only in Angular apps. If your app was created with the Angular CLI, then you don't need to take any additional steps. Otherwise, make sure to install the peer dependencies of @interopio/ng:

{
    "dependencies": {
        "@angular/common": "^9.1.3",
        "@angular/core": "^9.1.3",
        "rxjs": "^6.5.5",
        "tslib": "^1.10.0"
    }
}

⚠️ Note that @interopio/ng v4 supports Angular 14+, while @interopio/ng v3 supports Angular 7-15 (inclusive).

The following example assumes that your app was created with the Angular CLI. Install the @interopio/ng library:

npm install --save @interopio/ng

Library Features

The @interopio/ng library exposes two important elements:

  • IOConnectNg - an Angular module that initializes the @interopio/desktop library;
  • IOConnectStore - an Angular service that gives access to the io.Connect APIs;

IOConnectNg Module

The IOConnectNg module is responsible for initializing the @interopio/desktop library. You must import the IOConnectNg module once for the entire app - in the root module by using the forRoot() method. This method accepts a settings object which has the following signature:

interface IOConnectNgSettings {
    holdInit?: boolean;
    browser?: {
        factory?: IOConnectBrowserFactoryFunction;
        config?: IOConnectBrowser.Config;
    };
    browserPlatform?: {
        factory?: IOConnectBrowserPlatformFactoryFunction;
        config?: IOConnectBrowserPlatform.Config;
    };
    desktop?: {
        factory?: IODesktopFactoryFunction;
        config?: IOConnectDesktop.Config;
    };
}

The following table describes the properties of the IOConnectNgSettings object:

Property Type Description
browser object Object with two properties: config and factory. The config property accepts a configuration object for the @interopio/browser library. The factory property accepts the factory function exposed by the @interopio/browser library. You should define this object if your app is a Browser Client app in the context of io.Connect Browser.
browserPlatform object Object with two properties: config and factory. The config property accepts a configuration object for the @interopio/browser-platform library. The factory property accepts the factory function exposed by the @interopio/browser-platform library. You should define this object if your app is a Main app in the context of io.Connect Browser.
desktop object Object with two properties: config and factory. The config property accepts a configuration object for the @interopio/desktop library used in io.Connect Desktop. The factory property accepts the factory function exposed by the @interopio/desktop library. You should define this object if your app is an io.Connect Desktop app.
holdInit boolean Flag indicating whether your app initialization must wait for the io.Connect factory function to resolve. Defaults to true.

⚠️ Note that you can't define a browser and browserPlatform property at the same time, but you can define one of them together with desktop. This is useful if you want your app to have different initialization characteristics in io.Connect Browser and io.Connect Desktop.

All properties are optional, but it's recommended that you provide the factory functions explicitly. If no factory functions are provided, the library will try to select the appropriate function injected in the global window object.

The initialization of the @interopio/desktop library is asynchronous and therefore can take anywhere between a few milliseconds and a couple of seconds. There are two main situations in which setting holdInit to true (default) or false will benefit your project:

  • holdInit: false - If the io.Connect functionalities play only a supporting role in your project, rather than being an essential part of it, it's recommended that you set holdInit to false. This way, your app won't have to wait for the io.Connect library to initialize in order to be able to function properly. You can use the IOConnectStore service to get notified when the @interopio/desktop library is ready.

  • holdInit: true - If the io.Connect functionalities are a critical part your project, then it's recommended to leave holdInit set to true. This way, Angular will wait for the io.Connect factory function to resolve before bootstrapping your first component. This will spare you the need to check whether the @interopio/desktop library is available every time you want to use it in your app. As a negative result to this approach, when your users load the app, they will keep seeing a blank screen up until the first component has been bootstrapped. Of course, you can solve this by providing a loader animation as soon as your app is accessed.

The example below demonstrates how to initialize the @interopio/desktop library:

import { IOConnectNg } from "@interopio/ng";
import IODesktop from "@interopio/desktop";

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        IOConnectNg.forRoot({ desktop: { factory: IODesktop } })
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

Note that if the io.Connect initialization fails for any reason (invalid configuration, missing factory function, connection problems or initialization timeout), your app will still initialize.

IOConnectStore Service

The IOConnectStore service is used to obtain the io object which exposes the io.Connect API. This service can also notify you when the @interopio/desktop library has been initialized and enables you to check for any initialization errors.

Example of creating an IOConnectStore service:

import { Injectable } from "@angular/core";
import { IOConnectStore } from "@interopio/ng";

@Injectable()
export class IOConnectService {
    constructor(private readonly ioConnectStore: IOConnectStore) { }
}

The IOConnectStore service offers the following methods:

Method Description
this.ioConnectStore.ready() Returns an Observable. Subscribe to it to get notified when the @interopio/desktop has been initialized. If the initialization fails, you will receive an object with an error property, otherwise the object will be empty. This is particularly useful if you set holdInit to false when initializing the library, because you need to make sure that the io.Connect library is ready for use before accessing any of its APIs.
this.ioConnectStore.getInitError() Returns an initialization error object from the io.Connect factory function or undefined.
this.ioConnectStore.getIOConnect() Returns the io.Connect API object. If needed, it's up to you to cast the returned object to the respective type (either IOConnectDesktop.API or IOConnectBrowser.API depending on the used io.Connect JavaScript library).

You can now inject the service in the components that need it and access the io.Connect API from the object returned by this.ioConnectStore.getIOConnect(). This gives you a decent level of encapsulation and control. If you prefer handling async actions with an Observable, then this service is the perfect place to wrap in an Observable the method you want to use.

Usage

The following examples demonstrate initializing and using the @interopio/ng library.

Initialization

Import the IOConnectNg module in the root module of your app and pass an io.Connect factory function:

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppComponent } from "./app.component";
import { IOConnectNg } from "@interopio/ng";
import IODesktop from "@interopio/desktop";

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        IOConnectNg.forRoot({ desktop: { factory: IODesktop } })
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

Consuming io.Connect APIs

Inject the IOConnectStore service in your component or service of choice in order to use the io.Connect API. It's recommended that you create your own Angular service that injects IOConnectStore and exposes only the functionality your app needs.

When initializing the @interopio/desktop library with the IOConnectNg module, you can use the holdInit property (see IOConnectNg Module) to configure whether the Angular framework must wait for the io.Connect factory function to resolve before bootstrapping your first component. Depending on this setting, you can use the IOConnectStore service in different ways. The following examples demonstrate both cases:

Creating the service:

import { Injectable } from "@angular/core";
import { IOConnectStore } from "@interopio/ng";

@Injectable()
export class IOConnectService {

    constructor(private readonly ioConnectStore: IOConnectStore) { }

    public get ioConnectAvailable() {
        return !this.ioConnectStore.getInitError();
    }

    public registerMethod(name: string, callback: () => void): Promise<void> {
        if (!this.ioConnectAvailable) {
            return Promise.reject("io.Connect wasn't initialized.");
        }
        return this.ioConnectStore.getIOConnect().interop.register(name, callback);
    }
}

Using the service:

import { Component, OnInit } from "@angular/core";
import { IOConnectService } from "./my-io-connect-service";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {

    constructor(private ioConnectService: IOConnectService) { }

    public ngOnInit(): void {
        if (!this.ioConnectService.ioConnectAvailable) {
            // There has been an error during the io.Connect initialization.
            return;
        }
        // io.Connect has been initialized without errors and is ready to use.
    }
}

If you set holdInit to true (default), you can be sure that everywhere you inject the IOConnectStore service, the respective properties will be initialized and set. This is very convenient, because you don't have to subscribe and wait for an event in order to use the @interopio/desktop library. However, you do need to always check if there is an initialization error by using this.ioConnectStore.getInitError(). If the io.Connect factory function throws an error, your app won't crash, but the io.Connect won't be available and the value returned by getInitError() will be set to the respective error object during initialization.

Creating the service:

import { Injectable } from "@angular/core";
import { IOConnectStore } from "@interopio/ng";

@Injectable()
export class IOConnectService {

    constructor(private readonly ioConnectStore: IOConnectStore) { }

    public ready() {
        return this.ioConnectStore.ready;
    }

    public registerMethod(name: string, callback: () => void): Promise<void> {
        return this.ioConnectStore.getIOConnect().interop.register(name, callback);
    }
}

Using the service:

import { Component, OnInit } from "@angular/core";
import { IOConnectService } from "./my-io-connect-service";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {

    constructor(private ioConnectService: IOConnectService) { }

    public ngOnInit(): void {
        // Show the loader.
        this.ioConnectService
            .ready()
            .subscribe((ioConnectStatus) => {
                if (ioConnectStatus.error) {
                    // Hide the loader.
                    // io.Connect isn't available.
                    return;
                }
                // Hide the loader.
                // io.Connect is ready, continue with your logic.
            })
    }
}

As you can see, this approach requires a little bit more code, but it gives you an easy way to provide pleasant user experience while io.Connect is initializing, handle gracefully any initialization errors, and when the initialization resolves normally, you don't need to check the error object as in the previous example.

App Definition

To add your Angular app to the io.Connect launcher, you must create a JSON file with app definition. Place this file in the %LocalAppData%/interop.io/io.Connect Desktop/UserData/<ENV>-<REG>/apps folder, where <ENV>-<REG> represents the environment and region of io.Connect Desktop (e.g., DEMO-INTEROP.IO).

The following is an example definition of an Angular app:

{
    "name": "my-app",
    "title": "My App",
    "type": "window",
    "details": {
        "url": "https://example.com/my-app/",
        "mode": "tab",
        "width": 500,
        "height": 400
    }
}

The "name", "type" and "url" properties are required and "type" must be set to "window". The "url" property points to the location of the web app.

The value of the "title" property will be used as a name for the app in the io.Connect launcher and as a window title if the web app doesn't have a document title.

For more details, see the Developers > Configuration > Application section.

io.Connect JavaScript Capabilities

Once the @interopio/ng library has been initialized, your app has access to all io.Connect functionalities. For more detailed information on the different io.Connect capabilities and APIs, see:

Reference

For a complete list of the available JavaScript APIs, see the io.Connect JavaScript Reference Documentation.