Appearance
Ogma-vue wrapper
We provide a seamless integration between Vue3 and Ogma. It is available here: linkurious/ogma-vue. Or via npm
:
sh
npm install @linkurious/ogma-vue@0.1.3
Please checkout the Ogma-vue documentation for further details.
From scratch tutorial
If you don't want to use Ogma-vue
and create your own wrapper, this tutorial will show you some guidelines. This tutorial will guide you for the integration of Ogma inside your Vue application. It is meant to be a build-from-scratch tutorial.
The goal of this tutorial is to provide a simple Vue application with:
- a component that displays the instance of Ogma
- a button to add a node
- a reactive message box that shows the current selection
Creating the app
In order to create the app the vue-cli
package will be used here:
npx @vue/cli create vue-ogma-project
The application is now ready, so it is possible to go into the new application folder and install the Ogma package.
npm install --save https://get.linkurio.us/api/get/npm/ogma/<VERSION>/?secret=<YOUR_API_KEY>
For alternative methods of installing Ogma, please refer to the getting started guide;
The Ogma component
The Ogma component will provide a simple template node that the Ogma library will use to create the canvas. When the template will be mounted on the page, then a Ogma instance is created and the provided data is used to create a graph representation.
vue
<template>
<div
id="graph-container"
style="position: absolute; left: 0; top: 0; bottom: 0; right: 0;"
></div>
</template>
<script>
// point to the correct path in case of alternative installation steps here
import Ogma from '@linkurious/ogma/ogma';
export default {
name: 'OgmaGraph',
data() {
return {
ogma: null
};
},
mounted() {
// create the instance of Ogma
this.ogma = new Ogma({
container: 'graph-container'
});
// setup more things in here on the instance
this.ogma.setGraph(this.data);
this.ogma.layouts.force();
},
beforeDestroy() {
if (this.ogma) {
// remove all the things from the instance and destroy it
this.ogma.destroy();
}
}
};
</script>
Save the content as Ogma.vue
and pass to the App integration part.
Show the Ogma component in the page
Once the Ogma component is ready it is possible to use it inside the App.vue
as component:
vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<OgmaGraph :data="graph" />
</div>
</template>
<script>
import OgmaGraph from './components/Ogma.vue';
export default {
name: 'app',
data() {
return {
nodes: [{ id: 0 }, { id: 1 }],
edges: [{ id: '0-1', source: 0, target: 1 }]
};
},
computed: {
graph() {
return { nodes: this.nodes, edges: this.edges };
}
},
components: {
OgmaGraph
}
};
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
At this point the graph is shown in the page but it is not possible to add any new data to it, just drag and move.
Adding a Node to Ogma
In order to add a node to Ogma the following steps are required:
- add a new button component
- emit an event when the user clicks the button
- listen the event and save the new graph state
- execute the layout in Ogma when the graph state is propagated
The first step is pretty easy, writing a simple button component like this one:
vue
<template>
<button @click="$emit('add-node')">Add a node</button>
</template>
<script>
export default {
name: 'AddNodeButton'
};
</script>
Save this component as Button.vue
file. Now it's the turn to add the button component to the App.vue
:
vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<OgmaGraph :data="graph"/>
<div id="controls">
<AddNodeButton @add-node="onAddNode">
</div>
</div>
</template>
<script>
import OgmaGraph from './components/Ogma.vue'
import AddNodeButton from './components/Ogma.vue'
export default {
name: 'app',
data() {
return {
nodes: [{ id: 0 }, { id: 1 }],
edges: [{ id: "0-1", source: 0, target: 1 }]
};
},
computed: {
graph() {
return { nodes: this.nodes, edges: this.edges };
}
},
components: {
OgmaGraph,
AddNodeButton
}
methods: {
onAddNode(){
// create a new node and edge
const newNode = { id: this.nodes.length };
const newEdge = {
source: newNode.id - 1,
target: newNode.id,
id: `${newNode.id - 1}-${newNode.id}`
};
// update the state
this.nodes.push(newNode);
this.edges.push(newEdge);
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
#controls {
position: absolute;
top: 10px;
right: 10px;
line-height: 24px;
}
</style>
Last step is to make the OgmaGraph
component aware of the change and react appropriately:
vue
<template>
<div
id="graph-container"
style="position: absolute; left: 0; top: 0; bottom: 0; right: 0;"
></div>
</template>
<script>
import Ogma from '@linkurious/ogma';
// utility function conveniently added
function getLastOne(list) {
return list[list.length - 1];
}
export default {
name: 'OgmaGraph',
props: {
data: Object
},
data() {
return {
ogma: null
};
},
mounted() {
// create the instance of Ogma
this.ogma = new Ogma({
container: 'graph-container'
});
// ...
// refactor the load and layout code into a re-usable function
this.loadAndLayout(this.data.nodes, this.data.edges);
},
methods: {
// re-usable version for load and update events
loadAndLayout(nodes, edges) {
this.ogma.addNodes(nodes);
this.ogma.addEdges(edges);
this.ogma.layouts.force({ locate: true });
}
},
watch: {
data(newData) {
const lastNode = getLastOne(newData.nodes);
const lastEdge = getLastOne(newData.edges);
this.loadAndLayout([lastNode], [lastEdge]);
}
},
beforeDestroy() {
if (this.ogma) {
// ...
this.ogma.destroy();
}
}
};
</script>
At this point it is possible to click the button and see the OgmaGraph
component animated with a new node added to it.
Adding a reactive message box
The last part of the tutorial covers how to connect the part of the application with events triggered by the Ogma
instance. The goal is to have a message box on the left side of the page updated everytime the user clicks on a node.
The first step is to create intercept the Ogma
event of selection:
vue
<template>
<div
id="graph-container"
style="position: absolute; left: 0; top: 0; bottom: 0; right: 0;"
></div>
</template>
<script>
import Ogma from '@linkurious/ogma';
function getLastOne(list) {
return list[list.length - 1];
}
export default {
name: 'OgmaGraph',
props: {
data: Object
},
data() {
return {
ogma: null
};
},
mounted() {
// create the instance of Ogma
this.ogma = new Ogma({
container: 'graph-container'
});
// add listeners
this.ogma.events.on('nodesSelected', this.setSelection);
this.ogma.events.on('nodesUnselected', this.clearSelection);
this.loadAndLayout(this.data.nodes, this.data.edges);
},
methods: {
clearSelection() {
this.$emit('selection', null);
},
setSelection() {
this.$emit('selection', this.ogma.getSelectedNodes().getId());
},
loadAndLayout(nodes, edges) {
this.ogma.addNodes(nodes);
this.ogma.addEdges(edges);
this.ogma.layouts.force({ locate: true });
}
},
watch: {
data(newData) {
const lastNode = getLastOne(newData.nodes);
const lastEdge = getLastOne(newData.edges);
this.loadAndLayout([lastNode], [lastEdge]);
}
},
beforeDestroy() {
if (this.ogma) {
// remember to remove the listeners before the destroy
this.ogma.events.off([this.setSelection, this.clearSelection]);
this.ogma.destroy();
}
}
};
</script>
Now it's time to create the message box component that will show the message:
vue
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
name: 'InfoPanel',
props: {
selection: Array
},
data() {
return {
message: 'No node selected'
};
},
watch: {
selection(newSelection) {
console.log({ newSelection });
if (newSelection == null) {
this.message = 'No node selected';
return;
}
if (newSelection.length === 1) {
this.message = `Node ${newSelection[0]} selected`;
return;
}
this.message = `Nodes ${newSelection.join(', ')} selected`;
return;
}
}
};
</script>
Save this last component as InfoPanel.vue
. The last step is to connect the OgmaGraph
component with the InfoPanel
component, this can be done through the App.vue
component:
vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<OgmaGraph :data="graph" @selection="onSelection" />
<div id="controls">
<AddNodeButton @add-node="onAddNode" />
<InfoPanel :selection="selected" />
</div>
</div>
</template>
<script>
import OgmaGraph from './components/Ogma.vue';
import AddNodeButton from './components/Button.vue';
import InfoPanel from './components/InfoPanel.vue';
export default {
name: 'app',
data() {
return {
nodes: [{ id: 0 }, { id: 1 }],
edges: [{ id: '0-1', source: 0, target: 1 }],
selected: null
};
},
computed: {
graph() {
return { nodes: this.nodes, edges: this.edges };
}
},
components: {
OgmaGraph,
AddNodeButton,
InfoPanel
},
methods: {
onAddNode() {
const newNode = { id: this.nodes.length };
const newEdge = {
source: newNode.id - 1,
target: newNode.id,
id: `${newNode.id - 1}-${newNode.id}`
};
this.nodes.push(newNode);
this.edges.push(newEdge);
},
onSelection(sel) {
this.selected = sel;
}
}
};
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
#controls {
position: absolute;
top: 10px;
right: 10px;
line-height: 24px;
}
</style>
The App.vue
has now a new selected
data property which holds the selection state and propagates it to the InfoPanel
component.
The result: