Finsemble: Build An App Step By Step

Build A Finsemble App

A Step By Step Tutorial

This tutorial will take you through the process of building an application. We will start with the Seed Project. Please read gettingFinsemble if you have not already and follow the instructions for installing the Finsemble Seed Project.

Here we will learn the basics of configuring and coding a Finsemble application. We'll start with a basic but common use case - Account Management. Many applications must provide a way to list accounts and then examine an account's details. This provides a perfect use-case for Finsemble which can provide users with simultaneous views into multiple accounts, avoiding a frustration that we've all encountered - having to flip back and forth when juggling information between two accounts.

Anatomy of a Finsemble Application

Finsemble Applications consist of one or more parts:

  • System Components - Generally speaking, a component is a piece of UI that lives in a window within a Finsemble application. Finsemble contains several core components which are automatically available to developers. These include the Toolbar and Linker.
  • Custom Components - Developers will build their own components as part of their application.
  • Packaged Components - Additional and third-party components are available to include in your application. ChartIQ provides Charting and Chat components as well as prebuilt market data components powered by Xignite.
  • Core Microservices - Microservices are processes that run within a Finsemble Application. Components can access these services through APIs. Core microservices include windowManager, workspaceManager, dialogManager, storageManager and the eventRouter. Some managers are configurable. For instance, the StorageManager allows developers to implement their own scheme for storing data (i.e. saving to an external database).
  • Custom Microservices - Developers can write their own services as part of their application.
  • Packaged Microservices - Some packaged components require additional services. For instance, the chat component uses a Chat microservice.
  • Microservice clients - Every microservice has a corresponding JavaScript client API that can be used by components to access the service.
  • Config files - Services, components, the application and the Finsemble/OpenFin runtimes all use JSON config files to manage behavior.
  • Dev Runner - The Dev Runner packages all of these parts into the Finsemble Application, allowing developers to quickly compile, run, and debug their code.

With this anatomy in mind, the basic skills necessary to build a Finsemble app are:

  • Adding, Implementing and Configuring components
  • Using built-in services: Launcher, Workspace, Linker

This tutorial will provide background information about these concepts as well as instructions that will step you through the process of actually building a working application.

Step 1: Adding Finsemble Components

We will start by creating two initial components: accountList and accountDetail. The accountList component will contain a list of accounts for our fictional company. The accountDetail component will provide detailed information about specific accounts.

In Finsemble, every component resides in its own window. A Finsemble Application is an assembly of component windows that operate as peers. Each component is essentially a web page, except that it has access to hooks that allows it to interact with its peers. These component windows all reside within a windowing infrastructure that allows users to customize their work environment.

Since Finsemble Components are essentially web pages, they can literally be anything that you can run in a web page. You can build Finsemble Components in Angular, React, Web Components, JQuery, or any other UI framework. In fact, a Finsemble Component doesn't even need to be written. You can wrap any existing web page, even those that you don't control, into a Finsemble Component.

For the sake of simplicity, our sample components will be rudimentary. We'll write them with JQuery using simple imperative language. This is only in order to convey the concepts as clearly as possible. Once you complete the tutorial, you will not have to limit yourself to this basic model.

Command Line Interface (CLI)

Finsemble comes with a command line interface that streamlines many common tasks. Finsemble Applications can grow large, with many components and services. Using the Command Line Interface (CLI) ensures that a common directory structure and naming style, by generating consistent boilerplate.

Open a command line or terminal (we recommend GNU Bash) and navigate into the directory where you've cloned or forked the seed project:

> cd <seed-project-location

CLI commands should be run from the root directory of your project. Let's start by adding our two components:

> finsemble-cli add component accountList

> finsemble-cli add component accountDetail

These commands will create boilerplate JavaScript, CSS, and HTML files for each component. You can find this boilerplate in your-repo/src/components. An entry is also create in your-repo/configs/components.json.

Now run the app!

> npm run dev

Note it may take a few seconds for the app to start and if this is the first time running, it will have to download external assets such as the OpenFin environment.

Once the app has started, choose "accountList" or "accountDetail" from the "Apps" section of the toolbar. Your empty component window will appear. You can exit the application by quitting under the "File" section of the toolbar. Also "ctrl-c" in your command tool will stop the dev runner.

Configuring Components

Within your-repo/configs/components.json contains a configuration file that tells the Finsemble app which components to load and how they should be configured. The CLI will add and remove entries from this file. You can edit this file to modify behavior of components, and to control interaction between components.

Example components.json entry :

    "accountList": {
        "window": {
            "url": "\\components\\accountList\\accountList.html",
            "frame": false,
            "resizable": true,
            "autoShow": true,
            "defaultTop": "center",
            "defaultLeft": "center",
            "defaultWidth": 800,
            "defaultHeight": 600
        },
        "component": {
            "type": "accountList"
        },
        "foreign": {
            "services": {
                "workspaceService": {
                    "persists": true,
                    "isArrangable": true
                }
            },
            "components": {
                "App Launcher": {
                    "launchableByUser": true
                },
                "Window Manager": {
                    "FSBLHeader": true
                }
            }
        }

The Config File for a component must contain a url. It doesn't need to include anything more than that; however, the CLI automatically adds some default entries. In the config, each subsection provides a location specific to foreign services and foreign components. For instance, in the JSON above the "launcher" section provides information that the Launcher service would use to position the component. Likewise, the "linker" section provides configuration information for the Linker service. These sections are optional. You would only use the Linker service for components where it made sense to configure them.

Step 2: Create Some Component Code

Before going further, for this tutorial we've gone ahead and created code that you can cut & paste into your component files. Inside your-repo/src/tutorials you will find accountList and accountDetails directories. Go ahead and copy these files into your-repo/src/components.

> cp -r src/tutorials/* src/components

If you open the examples in your editor, you will see sections that have been commented out. As we step through this tutorial, we will ask you to uncomment each section along with an explanation for what that section of code is doing. For now, go ahead and run the application again. You can add each component to your workspace from the toolbar just to make sure that they are working correctly.

We've preloaded the accountList component with some dummy data. In real-life, you would fetch this data from a file or network service. You do not need to use any specific Finsemble methodology for fetching data. For instance, you can make Ajax requests, or WebSockets, just like any other web application. Later, we will discuss some advanced data use cases where Finsemble can provide specific benefits.

Below we'll walk you though some changes to your application.

Step 3: Modifying and Accessing Component Configuration

Let's first set up a custom configuration option and learn how to access that information programmatically. Open up your-repo/configs/components.json in an editor. Add the following line:

{
    "accountList": {
        ...
        "component": {
            "account-type": "vip",                <------------------ Add this line (but not this comment)
            ...
        }
        ...
    }
}

Now open your component JS file in your editor: your-repo/src/components/accountList/accountList.js

Comment out the following line in document.addEventListener towards the end of the file:

alert(FSBL.Clients.WindowClient.options.customData.component["account-type"]);

Now run the app again.

> npm run dev

Once the app has started, choose "accountList" from the "Apps" section of the toolbar. Your component window will appear and you should see "vip" pop up as an alert. After responding to the popup, you will see the account list displayed in the window.

Step 4: Removing components from the toolbar App List

Now that you've created these components, let's remove accountDetail from the App List. For the sake of this demo, we won't want users to launch this component directly; instead they will be clicking on accounts from the accountList to spawn it!

Open your_repo/configs/components.json in your favorite editor. Search for launchableByUser in the accountDetail section. Set this to "false".

For this change to take effect you will need to restart your application. Choose "Quit" from the application toolbar. Then "ctrl-c" in your command tool to stop the dev runner. Now type npm run dev to relaunch your application. accountDetail will no longer be available from the App List.

You only need to restart your application if you are making changes to the config files. When making code changes, the dev runner watches and automatically compiles changes. You only need to reload your component to get your changes (by right clicking and choosing reload).

Step 5: Using the Launcher service to spawn new components

Open up your_repo/src/components/accountList/accountList.js in your preferred editor. Search for the function launchAccountDetail. Go ahead and uncomment the code inside of this function. This code will spawn a new accountDetail component when the user clicks on a customer from the accountList.

FSBL.Clients.LauncherClient.spawn is the method that spawns new windows. FSBL.Clients is where you'll find the client APIs for all microservices. In this case, we are leveraging the Launcher service. Clients are static members of FSBL and they are always available when you include fsbl.js (this was done automatically for you when you added the component from the command line).

Note that you can control many aspects of the child window, with the second parameter to the spawn method, such as the position and size of the window. In the example code, we provide x and y coordinates for the position of the accountDetail component. See the getWindowLocation() function for how we calculate.

You can also use this parameter to send data to the child window. This is useful for bootstrapping the child. In our example, we've sent the current customer to the child window so that it initializes with the data the user has clicked on.

Example, send data to spawned client in second parameter to spawn() :

{
    component: {
        "accountNumber": accountNumber
    }
}

Spawning component windows is an asynchronous event, and so a callback is available for you to run commands once the new window has been created. You'll know if there has been an error. The second parameter, response, contains the config for the newly spawned component, which can be examined for information about the component.

Finally, we can set the title of the window dynamically with FSBL.Clients.WindowClient.setWindowTitle(). In our accountDetail.js, we set the title to be the currently enabled account.

Step 6: Advanced Spawning

The only problem with the current spawning code is that it creates a new window every time you click on a customer. Ideally, what we want is to spawn a component only if it doesn't already exist. If the component does exist, then just tell it to switch customers.

Uncomment the code in the launchAccountDetailAdvanced() function. (Note, we're using a little flag advancedIsRunning just for the purposes of this tutorial. You can ignore that).

This code first checks to see if the accountDetail component is already running. You might be tempted to simply create a local variable to remember if you've spawned it or not - but that is dangerous. A user could have closed the window. In other use cases, a user may have opened the window manually. The best approach is to actually look to see if the window is open. We do this by getting the list of active windows from the LauncherClient:

FSBL.Clients.LauncherClient.getActiveDescriptors() - returns descriptors for all the running windows. We iterate through these looking for a component of type "accountDetail". If it doesn't exist, then spawn the component like normal.

If the component is already open then we need to tell it to load the new customer. We do so by sending a message using FSBL.Clients.RouterClient.transmit(). The first parameter to transmit() is a channel name. By using the foreign component's window name, we effectively create a private communication with that component. If you check accountDetail.js, you'll see that we've set up a listener on this "private" channel to accept customer changes.

Note: there is no such thing as truly private communications within a Finsemble Application. The components within an application are assumed to trust one another. If you need to establish true secure communication you would need to use encryption just as you would with any network communication.

Step 7: Communicating between components

In the previous example, we demonstrated the most basic form of communication within a Finsemble app, which is a simple transmit and receive on a known channel. Here we will introduce a more advanced type of communication - query/response.

Search for the function communicateBetweenComponents and uncomment the code inside of this function in both accountDetail.js and accountList.js. This code will allow communication between the account Detail and accountList components.

In accountList, the parent, we set up a "responder" to listen on a new channel called "accountTraversal". Responders accept inbound requests and then return a response, much like a miniature http server. Our simple responder accepts requests to iterate forward and backward through the list of customers. The response for the request is the new customer number.

In accountDetail, the child, we make a "query" on the accountTraversal channel whenever the user clicks on the "next" button. When we receive the new customer, we display it.

This is a contrived example, but it shows the basic premise, that components can be programmed to interact with one another. Along with this query/response example and the transmit example from Step 3, Finsemble supports a pub/sub model which provides stateful communication.

Step 8: Linking components

Components can be synchronized by linking them. Linking is generally a user level activity, but as a developer you need to specify what data points your component can link to/from. You also need to respond to inbound linkage changes.

In our example, we'll create a third component called "accountStatement" which will show the customer's fictional billing statement. Users will typically want to link up the accountDetail and accountStatement components so that they stay synchronized. We'll step you through how to enable linking at the component level, and then how to create the linkage from a user level.

You may be wondering why we wouldn't just use events to link the components together like we did with accountList and accountDetail. This is certainly possible but it prevents the user from choosing whether they wish to link the components. User choice becomes very useful when you give users the ability to open many of the same component. An accountDetail window could be a slave to any number of parent windows for instance.

Stop your app ("Quit" from the toolbar; "ctrl-c" from your terminal window). Now add a new component:

> finsemble-cli add component accountStatement

Copy src/tutorials/accountStatement to src/components/accountStatement

Next, let's enable the linker for the accountDetail and accountStatement components. Edit configs/components.json. For each of these components add the following line to the foreign/components/Window Manager section:

"showLinker": true

Now restart your app (npm run dev). You can open up an accountStatement component from the Apps menu. Notice that it has a linker icon in the top left of the title bar. If you click on a customer in the accountList to open an accountDetail, you should now have a linker icon here as well. If you click on the icon, you'll see a string of colors. We link two components by choosing the same color for each component.

It's pretty simple, and the accountDetail.js and accountStatement.js files are already rigged up so you can try it out. Start the app up. Open an accountDetail window. Now choose the same color from the linker icon menu for both accountDetail and accountStatement. Now, anytime the customer changes on accountDetail, the accountStatement will also change.

How does it work? In accountStatement.js you will see this line:

FSBL.Clients.LinkerClient.subscribe("account", function(obj)... - This subscribes to linkages for the dataType called "account". The dataType is arbitrary, it's just a string that you specify. Whenever an event comes in, the callback will be called. Again, the primary difference now is that events only come when a user has explicitly linked the component.

Meanwhile, in accountDetail.js you will see this line:

FSBL.Clients.LinkerClient.publish("account", accountNumber); - this publishes the account number. We've put it inside of setAccountNumber so that anytime the number changes, from anywhere, the linkage is triggered.

Note: Linkages can be made bi-directional by adding both publish and subscribe calls to your component.

Step 9: Making components aware of Workspaces

You may have noticed that when you restart your app, the accountList component remains on the screen in the same location that you left it. This is because component windows belong to a "workspace". The state of all windows in your application is saved into a special workspace called "activeWorkspace". Users can also save their desktop, to create a saved workspace.

Finsemble automatically keeps track of window locations and does the work necessary to restore components to their proper position on the screen. However, if you want the actual state of your component to be restored, then you will need to provide workspace hooks in your component code. You will need to tell Finsemble when your app state changes, and you will need to initialize your application's state during startup.

Uncomment the functions getState() and setState() in accountList.js and accountDetail.js. These methods are used to save and restore the state of the component. We make a call to setState() whenever the state changes. This is captured and saved with the current workspace. We call getState() when the component starts, to initialize the state of the component. You can save any number of fields, and the value of the field can be either a complex object or a primitive (here we're just using a string to represent the account number).

Within these functions you'll see that we use the API commands FSBL.Clients.WindowClient.saveComponentState and FSBL.Clients.WindowClient.getComponentState. You can save either objects or primitives. This information gets stored with the component configuration in the workspace JSON. If it's convenient, you can store multiple pieces of state data under different field names. Please note that the get method is asynchronous.

Now if you quit and restart your application, the account numbers in each window should be the same as where you left off.

Setting Up A Default Workspace

By default, a Finsemble Application will start up with just the toolbar. You'll probably want your application to begin with one or more open components. The way to accomplish this is to set up a default workspace.

Open your_repo/configs/defaultWorkspaces.json. Change it to the following code:

{
    "workspaces": [
        {
            "name": "Default Workspace with No Windows",
            "windows": [],
            "components": [
                {
                    "type": "accountList",
                    "options": {
                        "defaultTop": 300,
                        "defaultLeft": 300
                    }
                }
            ]
        }
    ]
}

This tells Finsemble to open up an accountList component on initialization. We've provided pixel locations for the window. You can add as many windows as you'd like, just include them in the array. As you might have guessed, this configuration is merged (extended) into the general configuration for your components from components.json.

In order to test your default workspace, you'll need to clear out your localStorage. With your app running, right click any component to open a debugger window. From the console, type localStorage.clear(). Now quit your application without moving any windows. Restart your dev runner. Your application should start up and display a single accountList component on the screen.

The default workspace is only used once. After loading the application for the first time, Finsemble saves to activeWorkspace which supercedes the default.

Step 10: Working With Hosted Components

So far we've been working with local components - those that you build and bundle directly into your application. Finsemble can load components dynamically, from servers on your network or from the Internet. This is useful in several circumstances:

  • You can include web pages in your Finsemble Application
  • You can modify your existing web applications to be Finsemble aware
  • You can load third party components

Currently, ChartIQ has made available the following advanced components:

  • Advanced Charting - Provides an example of ChartIQ's advanced charting incorporated into a component and made Finsemble aware. Charts remember their state and can be linked together.

    finsemble.chartiq.com/fin/components/premium/chart/chartiq-desktop.html

  • Chat - Provides an example chat component. This component is currently integrated with Slack. If you have a slack account you can use it right out of the box.

    finsemble.chartiq.com/fin/components/premium/chat/chat.html

Over time, this library will expand. If you'd like to see these components integrated with live market data in a sample application, please contact finsemble@chartiq.com and we can provide access!

Let's start by setting up a chat component!

Add the following code to your components.json config file:

    "Chat": {
        "window": {
            "url": "http://finsemble.chartiq.com/fin/components/premium/chat/chat.html",
            "name": "mainChat",
            "defaultTop": 200,
            "defaultLeft": 100,
            "defaultWidth": 180,
            "defaultHeight": 400,
            "minWidth": 180,
            "minHeight": 400
        },
        "component": {
            "type": "Chat"
        },
        "foreign": {
            "services": {
            },
            "components": {
                "App Launcher": {
                    "launchableByUser": false
                },
                "Window Manager": {
                    "FSBLHeader": true
                }
            }
        }
    }

Restart your application. You should now see a chat "blurb" icon in the toolbar (top right corner of your screen). Click on the blurb to open up chat. You can log in to the finsemble.slack.com team to discuss Finsemble development!

Here's the config for adding a Chart:

    "Chart": {
        "window": {
            "url": "http://finsemble.chartiq.com/fin/components/premium/chart/chartiq-desktop.html",
            "defaultTop": 200,
            "defaultLeft": 100,
            "defaultWidth": 640,
            "defaultHeight": 480,
            "minWidth": 180,
            "minHeight": 400
        },
        "component": {
            "type": "Chart"
        },
        "foreign": {
            "services": {
            },
            "components": {
                "App Launcher": {
                    "launchableByUser": true
                },
                "Window Manager": {
                    "FSBLHeader": true
                }
            }
        }
    },

Restart your app and then go ahead and open a couple of chart windows. Try using the linker to link them. Charts are linked by symbol, so when you change the stock symbol for one chart the other will follow. You can set one chart to be 1D and another to be 1m in order to see short term and long term views simultaneously as you flip through stocks.


Next Steps

For more advanced reading