Event dispatcher - Infomaker/Dashboard-Plugin GitHub Wiki

Event dispatcher

Agents, Widgets and Applications are very loose coupled in a Dashboard plugin, but it is still very easy to communicate between your classes, with the Dashboard and even with other plugins.

Send event (Simple)

We will start in your agent class. Let's say that you want to send an event to your application based on if your api is up and running. We would then write the following code:

this.send("pluginName:eventName", {status: true})

Send event (Advance)

As default all events are broadcasted, to everyone listening to the event name. If you want to target a specific type of plugin or even an instance of an app, widget or agent you can send an extended event. For example when you want to respond to a previous event.

Target an plugin type (via bundle id)
this.send({
	name: "pluginName:eventName", // Name of the event
	target: "se.infomaker.Example", // The bundle id of the target type
	userData: {
		status: true
	}
})
Target an app, widget or agent instance (via bundle id)
this.send({
	name: "pluginName:eventName", // Name of the event
	targetUUID: "da23b9dd-1e43-487e-81b1-570a4da8288a", // The UUID of the target type (more info in **listen to events**)
	userData: {
		status: true
	}
})

We call the dispatch send function exposed on this. We define the full event name, best practice here is to use your plugin name, followed by : and then what type of event.

An object is also passed to the send function containing the api status, we set it to true in this case.

Listen to events

To listen to this event inside your application class simply write

this.on("pluginName:apiStatus", userData => {
    console.log("Api is up and running", userData.status)
})

What is happening here is that we subscribe to the "pluginName:apiStatus" event. And we then asynchronously recieve the data in the userData object.

Waiting for other plugins

Plugins can be loaded, installed and activated in different order so if a plugin have dependency to another plugin we can use this.ready() to know when its ready to communicate with. For example if one plugin talks to an API and another plugin wants to use that data. We do this by listening to the bundle id of the target plugin. If the target plugin is already loaded the callback will fire instantly.

this.ready("se.infomaker.APIPlugin", () => {
	this.send("apiPlugin:search", {query: "dashboard"})	
})

Unregister dispatcher

When a component is unmounted from the DOM it is neccessary to unregister the dispatcher, otherwise callbacks can be called when your component is not mounted and this can cause your plugin to try to modify unmounted node elements. In other words, hell breaks loose.

But cheer up, this is taken care of the Dashboard 🎉 .

The dispatch off function is under the hood called in the componentWillUnmount function of your Application class.

❗️ If your override componentWillUnmount, you have to call this.off() by yourself.

Examples

Update an widget from an agent

We want to build a agent that holds a connection, the agent has no visual representation so we want to let the user know if we have a open connection or not. We use a widget to display the status of the connection and let the agent use the dispatcher to update the widget on its current status.

class PluginAgent extends Dashboard.Agent {
	constructor(props) {
		super(props)
	}

	connect() {
		// Connect to something...
		this.send("pluginName:status", {connected: true})
	}

	disconnect() {
		this.send("pluginName:status", {connected: false})
	}
}

class PluginWidget extends Dashboard.Widget {
	constructor(props) {
		super(props)

		this.state = {
			connected: false
		}
	}

	componentDidMount() {
		this.on("pluginName:status", userData => this.setState({connected: userData.connected}))
	}

	render() {
		return (
			<this.GUI.Paragraph text={"Connected: " + this.state.connected} />
		)
	}
}

Communicating between 2 plugins

In this case we have 2 different plugins, one that is the agent and handles API calls and the other is an application that want to use the API to display data from it. An application can have many instances, even on the same workspace. When the agent wants to communicate back to the application its important that it targets the right application instance and not all of them. By default the dispatch system adds the senders UUID to the userData to we can make a targeted event back.

// se.infomaker.APIPlugin
class APIAgent extends Dashboard.Agent {
	constructor(props) {
		super(props)

		this.on("APIPlugin:search", userData => {
			// userData.UUID is the sender of the event and is added by the dispatcher for all events
			this.search(userData.query, userData.UUID)
		})
	}

	search(query, UUID) {
		const data = // Fetch data from an API
		this.send({
			name: "APIPlugin:result",
			targetUUID: UUID,
			userData: {
				result: data
			}
		})
	}
}

// se.infomaker.APIClient
class APIApplication extends Dashboard.Application {
	constructor(props) {
		super(props)

		this.state = {
			result: []
		}
	}

	componentDidMount() {
		this.ready("se.infomaker.APIPlugin", () => {
			this.send("APIPlugin:search", { query: "dashboard" })
		})

		this.on("APIPlugin:result", userData => {
			this.setState({ result: userData.result })
		})
	}

	render() {
		return (
			// Display the results
		)
	}
}