Skip to content
  1. Tutorials

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:

  1. add a new button component
  2. emit an event when the user clicks the button
  3. listen the event and save the new graph state
  4. 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:

Ogma+Vue