How can I refresh Cytoscape after add a new node? [duplicate] - cytoscape.js

Is there a function that resets the graph to it's freshly loaded state? I've tried cy.reset() but that just resets zoom and pan, not restoring to the virgin graph.
Also is there a way to restore all removed elements?
Thanks!

Call cy.elements().remove() and cy.add() with the same graph data.

Save in a variable the elements you removed, e.g.:
const elsRemoved = cy.elements().remove();
Then, for restoring them simply call:
elsRemoved.restore();
Refer to https://js.cytoscape.org/#eles.restore for more info

With data (here, a .cyjs file exported from Cytoscape), loaded via a function into Cytoscape.js, and redrawn at the click of a button.
<head>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
</head>
<body>
<div id='cy'></div>
<script>
$.getJSON("ontology2.cyjs", function (data) {
var cy = cytoscape({
container: document.getElementById('cy'),
elements: data.elements,
// ...
});
// Reset graph:
$('button.reset')
.on("click", function() {
cy.remove('node["*"]');
cy.add(data.elements);
});
});
</script>
<button class="reset">Reset</button>
<body>
Node toggle functionality via code by "Chiarg A.M." and suggestion by "nensimons" at Hide and Show Child Nodes on Node Tap Cytoscape

Related

Make PrimeVue Splitter to have dynamic two-way panel size

I am using PrimeVue Splitter with Vue3 and composition API. I want to make the size of each panel dynamic , so when I click a button they return to their original size.
This is what I have so far
<button #click="fixPanels()" >fix</button>
<Splitter style="height: 100%" class="mb-3">
<SplitterPanel :size=smallPanelSize >
Panel 1
</SplitterPanel>
<SplitterPanel :size=bigPanelSize >
Panel 2
</SplitterPanel>
</Splitter>
export default {
setup(){
let smallPanelSize = ref(30)
let bigPanelSize = ref(70)
const fixPanels = ()=>{
message.value = 30;
bigPanelSize.value = 70
}
return{smallPanelSize, bigPanelSize, fixPanels}
}
}
I can resize the panels freely, but when fixPanels is clicked, they dont return to their original size. I guess I have to use two-way binding somehow or v-model but I dont know how, there is no input here. If this is impossible, can you suggest a similar resizable panels component that the panel size is dynamically controlled?
Thank you
It seems that the SplitterPanel component does not handle reactive sizes. As a result, the prefix sizes are determined when the component is loaded and cannot be reset via reactivity.
Therefore, the solution is to reload the components to force the sizes to be reset.
For this, I created a Panels component which will be used only to display the panels and its reset button. The sizes will be retrieved via props and not assigned within the component. The resize button will send an event via emit in order to signal to the parent component the will to reload the component, in order to reset the sizes.
The parent component will create the two default size variables with ref and send them to the props. The event will also be retrieved to call the fixPanels function. The only subtlety is to manage to force the reload of the Panels component. For this, I added a key value to the Panels component which will be incremented each time the fixPanels function is called. The purpose of this variable is only to make the child think that the values of the component have changed and to create the need to reload it.
Just below, the two coded components that correspond to my explanations. (I used Vue 3 with the composition API and TypeScript, but you will probably know how to convert it to Vue 3 Option, if needed)
Panels.vue
<template>
<button #click="$emit('reload')">fix</button>
<Splitter style="height: 100%" class="mb-3">
<SplitterPanel :size="smallPanelSize">
Panel 1
</SplitterPanel>
<SplitterPanel :size="bigPanelSize">
Panel 2
</SplitterPanel>
</Splitter>
</template>
<script setup lang="ts">
import Splitter from "primevue/splitter";
import SplitterPanel from "primevue/splitterpanel";
const props = defineProps<{smallPanelSize: number, bigPanelSize:number}>();
</script>
ManagePanel.vue
<script setup lang="ts">
import { ref } from "vue";
import Panels from "./components/Panels.vue";
let small = ref(30);
let big = ref(70);
let componentKey = ref(0);
function forceRender() {
componentKey.value += 1;
}
function fixPanels() {
small.value = 30;
big.value = 70;
forceRender();
}
</script>
<template>
<Panels
:smallPanelSize="small"
:bigPanelSize="big"
:key="componentKey"
#reload="fixPanels"
/>
</template>

Vue replaces "open" attribute to value "open" in any tag

I'm using vue.js (v2.6.12) components in laravel blade templates.
For the project, I'm also using MathML in which I need to use the open attribute of <mfenced> tag to be set to some custom values. Here is the example of the math expressing in mathml.
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="[">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
But as soon as the page renders, the open attribute is converted into this open="open". I'm 100% sure there is no other library or script is loaded that updates like so, just plain vue. This actually breaks the math expression. So it looks like this:
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" open="open">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
Later I realized that not only in math expression, litaratily any tag, be it <div open="anything">...</div>, <span open="anything">...</span>, <custom-element open="something">...</custom-element> having open attribute behaves the same. even if I use v-pre attribute to exclude it from vue js templete compiler.
And this do not happen, as soon I disable the vue app initialization.
The question here are:
Why vue is changing the open attribute like so?
How can I stop this behaviour, to the entire page within the vue application area or at least where I choose (something like using v-pre), is there ary config or any other way around?
Why
In HTML spec there are some attributes called boolean attributes. Spec dictates what can be a value of such attribute:
If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.
The values "true" and "false" are not allowed on boolean attributes. To represent a false value, the attribute has to be omitted altogether.
open is one of the boolean attributes - it is defined for the <details> element
Problem with Vue 2 is, that it treats most of the boolean attributes as global - without considering the element it is placed on. Result is that open attribute is always rendered with value "open" or removed if the value is falsy (when v-binding). This is fixed in Vue 3 as shown in 2nd example...
How
The use of v-pre is the way to go but unfortunately for you there is a bug.
See this issue. The bug was already fixed with this commit(Sep 21, 2020) but it was not released yet...
example - the "With v-pre" should work in Vue version > 2.6.12
const vm = new Vue({
el: '#app',
data() {
return {
message: 'Hi!',
html: `<div open="[" close="]">Hi from html</div>`
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<div v-html="html"></div>
<div v-pre>
<p open="[" close="]">With v-pre</p>
</div>
</div>
example - it works in Vue 3 - open is treated as boolean attribute only if placed on <details>
const app = Vue.createApp({
data() {
return {
message: 'This works in Vue 3!',
}
},
})
app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.11/vue.global.js" integrity="sha512-1gHWIGJfX0pBsPJHfyoAV4NiZ0wjjE1regXVSwglTejjna0/x/XG8tg+i3ZAsDtuci24LLxW8azhp1+VYE5daw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<div open="[" close="]">{{ message }}</div>
<details open="[">
<summary>Details</summary>
open attribute on details element is treated as boolean (renders empty value)
</details>
</div>
One workaround is to create a directive (named "attr") that sets the attribute:
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
Then use it in your template like v-bind but with v-attr:
<mfenced v-attr:open="'['">
Vue.directive('attr', (el, binding) => el.setAttribute(binding.arg, binding.value || ''))
new Vue({ el: '#app' })
<script src="https://unpkg.com/vue#2.6.12"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="app">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>f</mi>
<mfenced close="]" v-attr:open="'['">
<mrow><mi>a</mi><mo>,</mo><mi>b</mi></mrow>
</mfenced>
</math>
</div>
I've found a simple hack to solve this problem.
Why hack?
Because it is eventually going to be fixed in the comming release as pointed by #Michal, so just a quick & dirty hack is enough for now to go for it.
What I did is I placed the math content in the content and also added it to the data attribute and replacing the original content after vue has done its bad work (sorry just using blade syntax here, but it will make sense). I keep it in both places just for SEO purposes.
The template where I need math expression to be displayed.
...
<div class="proxy-content" data-proxy-content="{{ $article->content }}">
{!! $article->content !!}
</div>
...
I was using it along with jQuery, but you can easily substitute with vue.js' $el. This is what it looks in my app.js file.
...
const app = new Vue({
el: '#app',
methods: {
proxyContent() {
// Set Proxy Content.
jQuery('.proxy-content').each((i, el) => {
const $el = jQuery(el);
$el.html( jQuery('<textarea />').html( $el.data('proxy-content')).text() );
});
}
loadMathJax() {
// Load & Initialize MathJax Library.
const script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://cdn.jsdelivr.net/npm/mathjax#3/es5/tex-mml-chtml.js";
document.getElementsByTagName("head")[0].appendChild(script);
}
}
mounted(){
// Enable proxy content after mount, so we are sure no more rendering issue for templates.
this.proxyContent();
// Load MathJax library with a little delay to make sure everything is ready before loading the library.
setTimeout(() => this.loadMathJax(), 10);
}
});
...
One might argue, that I'm mixing up things outside of the scope of the vue application. For me that is not an issue, as the whole page is using vue.js and also the single thing don't make any harm even if there is another scope that is using mathml (though it depends on actual implementation).
In that case, if you want to scope it well, just use $el of vue.

Riotjs data from child tag to parent tag

I have looked lot similar questions, but still have difficulties getting it done. Try-d observable-s but messing up somewhere and cant get it done. new to riotjs still
in child tag i have a function that pushes data to a list:
<make-list>
...lots of html...
<script>
var piclist = []; --after first function run this list has data
....
done: function (e, data) {
piclist.push(data.result);
}
...
</script>
</make-list>
and in parent data i want to access it in a function
<main>
...lots of html..
<script>
riot.mount('make-list')
and i wana use that piclist = []; list here inside a function
</script>
</main>
Got it done this way with using mixin. Maybe it's not the right way, but it works.
<main>
...lots of html..
<script>
riot.mount('make-list')
var piclist = [];
riot.mixin(piclist)
</script>
</main>
<make-list>
... lots of html ...
<script>
...
done: function (e, data) {
piclist.push(data.result);
}
...
</script>
</make-list>
It seems you want to have the make-list tag create list items for a list that is displayed by main and make-list is to be a child of main.
You are using riot.mount('make-list') within the parent tag. That is at least very unusual: It actually triggers all make-list tags on the page to be mounted. Why not go with the riot way and add it within the html part of the parent, like this?
<main>
... lots of html ...
<make-list opts={piclist} />
<script>
this.piclist = [];
</script>
</main>
The opts allow you to pass data to the child (in this case the reference to the list). You can access that within the child tag like so:
<make-list>
... lots of html ...
<script>
...
done: function (e, data) {
this.opts.piclist.push(data.result);
}
...
</script>
</make-list>
I hope this helps.
Take a look at RiotComponent. It enable communication between elements in an intuitive way.

How to control order of view activation in Durandal

I am building an application with Durandal 2.0.
My shell view looks like this:
<div>
<header id="nav" data-bind="compose: 'viewmodels/nav', activate: false">
</header>
<section id="content" class="main container-fluid" data-bind="router: { transition: 'entrance' }, activate: false" style="overflow: auto">
</section>
<footer>
<!--ko compose: {view: 'footer'} --><!--/ko-->
</footer>
</div>
In the nav section, I want to have my tabs and a drop-down list of users (both of which are retrieved from a web service). Selecting a user from the dropdown will navigate to a new URL which will update the content section. (the route looks something like localhost/#user1/tab2).
Problem: I need to know the selected user from the nav section before I can retrieve the data for the content section, but the content section is activating before I have retrieved the users.
This is really only an issue for the initial page load, since the user list is only retrieved once.
Is there a way to tell the content section to wait until the nav section is done loading?
Is there a better way to go about this than what I'm doing?
The nav activate function looks like this:
function activate(context) {
return dataservice.getUsers().then(function () {
//do stuff
});
});
This activate function gets called first, and dataservice.getUsers() is called, but then the activate function of the content module gets called before the "do stuff" part happens (and before the data from the getUsers call is returned in the dataservice). Maybe there's a problem with my promises?
Edit
I've put together a dFiddle with some actual code that shows what I'm talking about: http://jerrade.github.io/dFiddle-2.0/#test/dashboard
The code is here: https://github.com/jerrade/dFiddle-2.0
nav.js
function activate(context) {
console.log('Nav View Activated');
if (vm.impersonateUsername == undefined)
vm.impersonateUsername = getUsernameFromWindowLocation();
return dataservice.getPageDetailForEmployee(vm.loggedInUsername, vm).then(function () {
console.log("Page details retrieved");
// I want to do something here before the dashboard activates.
});
}
dashboard.js
function activate(username) {
console.log('Dashboard View Activated');
//vm.username = nav.selectedImpersonateEmployee().Username;
return dataservice.getDashboard(nav.impersonateUsername, dashboard);
}
Open the page and watch the console. You'll see (among other things)
Nav View Activated
Dashboard View Activated
Page details retrieved
What I really want is for the Page details to be retrieved before the Dashboard view activates. I've actually rejiggered things so that this isn't currently a problem anymore, but it may crop again down the road.
It doesn't seem like what I'm trying to do should be this complicated. Unless I'm pounding a square peg into a round hole here?
The easiest thing to do would be to add an observable called something like isUserLoaded on your view model. Then, apply an if binding around the content section:
<div>
<header id="nav" data-bind="compose: 'viewmodels/nav', activate: false">
</header>
<!-- ko if: isUserLoaded -->
<section id="content" class="main container-fluid" data-bind="router: { transition: 'entrance' }, activate: false" style="overflow: auto">
</section>
<!-- /ko -->
<footer>
<!--ko compose: {view: 'footer'} --><!--/ko-->
</footer>
</div>
Once you've loaded the user, you can update the observable to true and your content binding should fire.
Edit
If that does't work it sounds like your router must still be resolving the content view model (which may point to a routing problem? Hard to say without seeing your entire solution).
Anyway, if its not a routing issue, then you could employ a solution where your content model returns an unresolved promise from its activate method; then resolve that promise when the user is loaded. For example, something along these lines:
userModel.js:
define([], function () {
// single instance to track the loaded user
var userModel = {
selectedUser: ko.observable()
};
return userModel;
});
navModel.js:
define(["dataService", "userModel"], function(dataService, userModel) {
// view model for the nav bar
var navModel = {
// model definition
};
// load the user
navModel.activate = function() {
return dataService.getUsers().then(function(response) {
// Push the user to the userModel
userModel.selectedUser(response.user);
});
};
return navModel;
});
contentModel.js:
define(["userModel"], function (userModel) {
var contentModel = {
// model definition
};
contentModel.activate = function () {
// Prevent activation until user is resolved
var deferred = $.Deferred();
var subscription = userModel.selectedUser.subscribe(function (user) {
if (!user)
return;
subscription.dispose();
deferred.resolve();
});
return deferred.promise();
};
return contentModel;
});
If you don't want to use an observable for the userModel.selectedUser property, you could instead use another deferred, or fire an event when the user is loaded, etc etc. Whatever floats your boat!

Dojo declarative onclick not triggering setter

Dojo 1.8
If I have a widget that has an overridden onclick setter:
// in mypackage/ClickableWidget
_setOnClickAttr: function( onClick ) {
this.onClick = onClick;
// handle the event...
}
...then it get's invoked just fine when I create the widget programattically:
new ClickableWidget({
onClick: function() {
alert('clicked');
}
});
BUT... it seems when I create my widget declarativly:
<div data-dojo-type="mypackage/ClickableWidget">
<script type="dojo/on" data-dojo-event="click">
alert('clicked');
</script>
</div>
...it doesn't trigger my overridden onclick setter.
Is this just a fact of dojo, or am I doing something wrong?
also, is there a better term to refer to what I am calling my "overridden setter"?
Update 2
Maybe the way you write the script tag is wrong:
Find this :
<script type="dojo/method" data-dojo-event="onClick" data-dojo-args="evt">
Here's the link to the dojo-source: http://dojotoolkit.org/reference-guide/1.7/dojo/isFunction.html
My last guess. hope this helps.