rcarcasses / vue-cytoscape

cytoscape.js now inside vue.js
https://rcarcasses.github.io/vue-cytoscape
MIT License
96 stars 34 forks source link

Updating layout #49

Closed jsinkers closed 4 years ago

jsinkers commented 4 years ago

Following on from #48 and #22: Is there a way to run layout.run() if elements are updated? I'm having the issue that if I update elements, my layout resets such that all elements are in the top left corner.

Perhaps I can call the lifecycle hook afterCreated again from the parent, (I think this is what @rcarcasses alluded to in #22), but I'm not sure how to do this. I know I can call this.$refs.cy.afterCreated() but this won't pass in the reference to the cytoscape instance so I'm a bit stuck!

Could you please suggest how to update the layout after elements are updated?

rcarcasses commented 4 years ago

Hi @jsinkers, in afterCreated hook you can store a reference to the cy instance passed and use it later whenever you need. Let me know if it works for you.

jsinkers commented 4 years ago

Thanks for making that clear - that worked for me.
I stored a reference to cy instance like this:

async afterCreated(cy=null) {
    if (cy !== null) {
        this.cy = cy
    } else {
        cy = this.cy
    }
    await cy
    cy.layout(this.config.layout).run()
}
cbarokk commented 4 years ago

Hi, I have the same issue as James: nodes gather in top-left corner because layout is null I believe. However, the fix does not work for me: storing a reference to cy makes my script unresponsive for some reason I dont understand. Any idea appreciated.

rcarcasses commented 4 years ago

Hi @cbarokk, storing the reference is the right path to go, maybe something else is happening? If you can, please provide a minimal repo with the issue you are experiencing so we can help you further.

cbarokk commented 4 years ago

Thanks @rcarcasses What's happening to me is that the call this.cy = cy; is never returning. I'll investigate further and see if I can provide a minimal reproducible example. Thanks again for your responsiveness :)

cbarokk commented 4 years ago

So I cannot store the instance reference for some reason. The assignment call never returns. Definitely something else going on. So I tried a workaround like so.

` @Watch('elements', { deep: true }) public async onelementsChanged() { const cy = this.$refs.cyRef.instance; this.afterCreated(cy); }

async afterCreated(cy) { await cy; cy.layout(this.config.layout).run(); } ` but the layout still does not get updated.

rcarcasses commented 4 years ago

What's happening to me is that the call this.cy = cy; is never returning.

I'm not sure why it happens, I have take a look to my own code in some project and I found that what worked for me was the following:

<script>
// somewhere after imports and outside the default export
let resolveCy = null
export const cyPromise = new Promise(resolve => (resolveCy = resolve))

export default {
...
  methods: {
    afterCreated(cy) {
      resolveCy(cy)
    },
}
</script>

then you can use cyPromise anywhere in your code (you can import it into another component for instance):

const cy = await cyPromise
...

let me know if that helps and merry Christmas :).

cbarokk commented 4 years ago

Thanks a lot @rcarcasses ! It now works for me as well. Here is my setup.

<template>
...
<cytoscape :config="config" :afterCreated="afterCreated">
       <cy-element
          v-for="def in elements"
          :key="`${def.data.id}`"
          :definition="def"
        />
</cytoscape>
...
</template>

<script>
let resolveCy = null;
export const cyPromise = new Promise(resolve => (resolveCy = resolve));

...

export default class MyApp extends Vue {
...

@Watch('elements', { deep: true })
  public async onelementsChanged() {
    const cy = await cyPromise;
    cy.layout(this.config.layout).run();
  }

  async afterCreated(cy) {
    resolveCy(cy);
    cy.layout(this.config.layout).run();
  }

...
}
</script>

Thanks again and Merry Christmas to you too!