1
votes

I'm trying to integrate cytoscape into a vuetify SPA. Rendering a graph into a v-card-element is already working. What is also working is an on-click handler to navigate to a different page using the vue router when clicking a note in the graph. But when I replace the router-push with a command to open a context menu (v-menu), the context-menu never appears. I already double checked that the same context menu appears if triggered from a different place in the vue page.

Opening the context menu is implemented with a simple boolean value registered in vue which is bound to the v-model of the v-menu. By setting the value to true the context menu should open.

But that never happens when the value is set to true from within the cytoscape on-click handler.

The code is partially copied from the vuetify examples.

Here is the template:

<template>
  <v-container fluid>

    <v-layout row justify-center align-center>
      <v-flex xs10>
        <v-card>
            <v-toolbar>
              <v-btn @click="contextMenuOpen=true">Show Contextmenu</v-btn>
            </v-toolbar> 
          <div ref="cytoscape" class="cytoscape" style="width: 100%; height: 80vh;" id="cytopane"></div>
        </v-card>
      </v-flex>
    </v-layout>

    <v-menu v-model="contextMenuOpen" :position-x="x" :position-y="y" absolute offset-y>
      <v-list>
        <v-list-tile @click="doSomething()">
          <v-list-tile-title>Do Somethin</v-list-tile-title>
        </v-list-tile>
        <v-list-tile @click="doSomethingElse()">
          <v-list-tile-title>Do Someting</v-list-tile-title>
        </v-list-tile>
      </v-list>
    </v-menu>

  </v-container>
</template>

and the js part:

. . .
data: function () {
  return {
    contextMenuOpen: false,
    style: [
      {
        selector: 'node',
        style: {
          . . .
        }
      }, {
        selector: 'edge',
        style: {
          . . .
        }
      }
    ],
    layout: {
      name: 'cose-bilkent'
    }
  }
},
methods: {
  showContextMenu: function (x, y) {
    let vs = this
    this.contextMenuOpen = false
    this.x = x
    this.y = y
    this.$nextTick(() => {
      this.contextMenuOpen = true
    })
  },
  renderView: function (data) {
    let vs = this
    cytoscape.use(coseBilkent)
    let cy = cytoscape({
      container: document.getElementById(this.$refs['cytoscape'].id),
      elements: data,
      style: this.style,
      layout: this.layout
    })
    cy.on('click', 'node', function (evt) {
      vs.showContextMenu(evt.originalEvent.clientX, evt.originalEvent.clientY)
    })
  },
},
mounted() {
  . . . 
  this.renderView()
}

By clicking the button in the toolbar which I put in for testing the context menu would appear. What I find strange is that if the boolean value 'contextMenuOpen' is bound to the v-model of the menu and will be set to true from the on-click handler the value will be set back to false imediately. If I remove the variable from the v-model binding in the v-menu the value will stay true.

Is this a threading issue? What did I do wrong?

UPDATE: I have prepared a js-fiddle that reproduces the problem I have: https://jsfiddle.net/gofrm76/0tkjp3rs/63/

UPDATE2: By creating a watch on contextMenuOpen and setting a breakpoint inside this watch I was able to track the source of the problem down at least a little bit using the stack trace. In fact, the menu appears for a millisecond and closes right away because of a click-outside handler executed in the menu. This would normally happen when the context menu appears and I click a second time somewhere else on the screen to close it again. In the case of the cytoscape on-click event, I apparently miss some kind of event propagation stop because the one click on the node leads to the opening of the menu and the immediate trigger of the click-outside handler.

UPDATE3: I found a config option on the vuetify v-menu that disables the ability to close the context menu by clicking somewhere outside of it. By binding another bool variable to 'close-on-click' that is initially false and will be set to true after a short delay I circumvent the handling of the second event mentioned above and the menu stays open. After the delay, the close-on-click will be set back to the default 'true' and the user is able to close the menu by clicking outside of it.

So for me, this is a solution I can work with for now. It showed also that the assumption from update 2 about the two events is correct and it also shows that the original post is misleading/incorrect: v-menu works as expected but the actual problem is that there are two events being emitted from the "third party lib" / cytoscape.

That leaves me with the question: How can I prevent the second event?

2

2 Answers

0
votes

UPDATE :

After debugging your app i found that the issue has something to do with vuetify because your v-menu component was causing it .... after using a v-show on the previous one i found that the component had the following styles :

  min-width: 0px;
  transform-origin: left top 0px;
  z-index: 0;
  display: none;

Also ... the reason why your contextMenuOpen was converted immediately is because you had two statments that mutates the value on the same method (i commented the changes)

when i replaced v-menu with a normal div everything worked fine check it :

https://jsfiddle.net/jhuxwkec/

0
votes

Even though this question was asked a while ago, I recently encountered the same issue and would like to help those facing a similar issue. So, I realized that the vuetify menu with the absolute prop only works with @contextmenu and doesn't work as expected with @click. The reason is that the way the menu works is that it checks for a click outside the menu to close the menu. Thus, when you click on another button, the button emits an event trying to open the menu but at the same time, a click outside the menu is detected which results in the menu immediately closing. This problem does not occur with context menu as no click is detected. Thus the way I handled this was to just delay the opening of the menu on click so that it fires after the closing event fired when a click outside the menu is detected. A setTimeout will solve the issue.