How to get the id of the node where I drop the node that I am dragging - cytoscape.js

I am trying to use the compound-drag-and-drop extension of cytoscape to create an event where I need the id of the node where I am dropping the node that I am dragging. Instead the id that I am getting is a "parent" auto generated by the extension. Is there a way to get the id of my node so I can bind both of them when I drop the first one into the second one?

I think that is exactly what the custom event
cy.nodes().bind('cdnddrop', function (event, target){
var grabbedNodeID = event.target._private.data.id;
var droppedNodeID = target._private.data.id;
});
does, so try it out and see if it helps you.

Related

Create a square with additional data node data

I am pretty much a javascript noob, and very new to cytoscape (but i code in other languages)...
I am trying to use Cytoscape to graph out the servers available in my network. Creating a node per server and their relationship was straightforward actually.
I would like to add the possibility than when I click a node (let's say server12345) that it would open kind of a table with data associated with that server (servertype etc...).
Do you have any idea / examples / guidance on how I could:
add functionality on that click event of a specific node ?
how to create a 'details' table using cytoscope?
Thank you guys so much in advance!
(Ps: My first post on StackOverflow...)
Nice question.
Firstly, you can capture the click event of cytoscape.js like below
cy.nodes().on('click', function(e){
var clickedNode = e.target;
console.log(clickedNode.id());
});
or you can also use the tap event of cytoscape.js
cy.on('tap', 'node', function(evt){
var node = evt.target;
console.log( 'tapped ' + node.id() );
});
You can read events section of the documentation for more details
After you capture this event, I see to ways.
1-) Just modify an existing HTML element on the UI. For example in here you can see on the left side there are some other elements.
So you can write your code like
cy.on('tap', 'node', function(evt){
var node = evt.target;
document.getElementById('someOtherElementOnUI').innerHTML = JSON.stringfy(node.data());
});
This will simply modify some other UI element (HTML element) and changes its content to the data of the selected node.
2-) You can use some extensions like Cytoscape.js-popper and show data dynamically like a tooltip. You can check my answer https://stackoverflow.com/a/66813720/3209523

Aurelia Validation and event publish not updating values in View

I was using Event aggregate publish method to update view from another view. when I created publish and subscribe data was able to get in view model , but that is not updating in view with assigned variables. If I use JavaScript to fill value it's working but not able to trigger validate controller.
Expected result is:
From the subscribe method I have to fill the view values.
After that it should trigger validate on value changes.
In below gist run I have two view and view-model. One is registration-form and second one is data-form. In data-form I have table with data, on click of each row I am publishing one event with selected row data, and this published event I was subscribing in side of registration-form view-modal. For more details look into below gist run.
Gist run: https://gist.run/?id=8416e8ca1b9b5ff318b79ec94fd3220c
Two possible solutions: (to your "undefined" alert)
Change alert(this.email); to alert(myself.email);
Use the thick arrow syntax for the subscribe function. Change:
this.ea.subscribe('MyrowData', function(obj){
to
this.ea.subscribe('MyrowData', obj => {
This syntax allows the new function to preserve the context/scope of the parent, which means that this.email will still be accessible.
Another suggestion:
You could simplify your data-form.js function as follows:
Change:
this.eventAggregator.publish('MyrowData', this.items[this.items.indexOf(item)]);
to:
this.eventAggregator.publish('MyrowData', item);
This will work because you've already provided item in your function call click.delegate="PopulateData(item, $event)".
In fact, you could even delete $event and event from the two PopulateData definitions (in data-form.js and data-form.html).
When I implement these suggestions, your data is also validated correctly.

Adding an item to a dojo store using 'before' option

I've got a dijit/Tree connected to a dijit/Tree/ObjectStoreModel which is in turn connected to a dojo/store/Memory wrapped in dojo/store/Observable - essentially as per the example on the dijit/tree documentation.
It's working mostly fine: I preload the store with some objects and can add other objects in using store.add(item). The data is hierarchical and that also is working by setting the parent property on each item added to the store.
However, I want to be able to specify how within a parent sub items should be ordered. e.g. if I've got an item with id "parent_1" and I add two items (say, "item_A", "item_B") both with parent set to "parent_1" then I'll end up with:
parent_1
item_A
item_B
However, I want item_B to be placed above item_A.
store.add supports a second parameter: an object of properties, one of which is 'before'. My understanding is that when you set 'before' to another object in the store, it should be placed before it. so, when I add item_B I use:
var item_A = store.get("item_A");
var item_B = { id: "item_B", parent: "parent_1", ... };
store.add(item_B, { before: item_A });
However, it's not working. item_B is always simply appended at the end. Does anyone know how to make this work? Thanks!
Looking at the source, it doesn't look as if dojo/store/Memory supports the before PutDirective.
If you want this feature, I suspect you will have to submit a Dojo feature request, or patch/augment dojo/store/Memory to provide the function you require.

Dojo dnd (drag and drop) 1.7.2 - How to maintain a separate (non-dojo-dnd) list?

I'm using Dojo dnd version 1.7.2 and it's generally working really well. I'm happy.
My app maintains many arrays of items, and as the user drags and drops items around, I need to ensure that my arrays are updated to reflect the contents the user is seeing.
In order to accomplish this, I think I need to run some code around the time of Source.onDndDrop
If I use dojo.connect to set up a handler on my Source for onDndDrop or onDrop, my code seems to get called too late. That is, the source that's passed to the handler doesn't actually have the item in it any more.
This is a problem because I want to call source.getItem(nodes[0].id) to get at the actual data that's being dragged around so I can find it in my arrays and update those arrays to reflect the change the user is making.
Perhaps I'm going about this wrong; and there's a better way?
Ok, I found a good way to do this. A hint was found in this answer to a different question:
https://stackoverflow.com/a/1635554/573110
My successful sequence of calls is basically:
var source = new dojo.dnd.Source( element, creationParams );
var dropHandler = function(source,nodes,copy){
var o = source.getItem(nodes[0].id); // 0 is cool here because singular:true.
// party on o.data ...
this.oldDrop(source,nodes,copy);
}
source.oldDrop = source.onDrop;
source.onDrop = dropHandler;
This ensures that the new implementation of onDrop (dropHandler) is called right before the previously installed one.
Kind'a shooting a blank i guess, there are a few different implementations of the dndSource. But there are a some things one needs to know about the events / checkfunctions that are called during the mouseover / dnddrop.
One approach would be to setup checkAcceptance(source, nodes) for any target you may have. Then keep a reference of the nodes currently dragged. Gets tricky though, with multiple containers that has dynamic contents.
Setup your Source, whilst overriding the checkAcceptance and use a known, (perhaps global) variable to keep track.
var lastReference = null;
var target = dojo.dnd.Source(node, {
checkAcceptance(source, nodes) : function() {
// this is called when 'nodes' are attempted dropped - on mouseover
lastReference = source.getItem(nodes[0].id)
// returning boolean here will either green-light or deny your drop
// use fallback (default) behavior like so:
return this.inhertied(arguments);
}
});
Best approach might just be like this - you get both target and source plus nodes at hand, however you need to find out which is the right stack to look for the node in. I believe it is published at same time as the event (onDrop) youre allready using:
dojo.subscribe("/dnd/drop", function(source, nodes, copy, target) {
// figure out your source container id and target dropzone id
// do stuff with nodes
var itemId = nodes[0].id
}
Available mechanics/topics through dojo.subscribe and events are listed here
http://dojotoolkit.org/reference-guide/1.7/dojo/dnd.html#manager

ExtJS 4: Add children tree nodes to a non-Root node dynamically?

I am trying to add children tree nodes to a non-Root node dynamically when it's selected.
This is how I implemented it:
First, I populate my tree with a TreeStore then to dynamically add children tree nodes, I handle it on the itemexpand event in tree Controller.
//pThis is a NodeInterface obj of the selected node
itemexpand: function (pThis, pEOpts) {
//type is an attribute that I added so that I know what type of node that got selected
if (pThis.raw.type === 'Staff' && !pThis.hasChildNodes()) {
var staffNodeAry = [{text:"Teachers", type:"ChildStaff", leaf: false},
{text:"Principals", type:"ChildStaff", leaf: false}];
pThis.appendChild(staffNodeAry);
}
}
This worked quite good because I am able to see both "Teachers" and "Principals" node under "Staff" type but there are 2 issues that I found so far:
1) There's this error on the console:
Uncaught TypeError: Cannot read property 'internalId' of undefined
I don't know why but if anyone could enlighten that would be great.
2) When I expand either "Teachers" and "Principals", the NodeInterface obj that's returned by itemexpand has an undefined raw object.
So I couldn't do this: pThis.raw.type
From my understanding, I don't need to create a new TreeStore to add any new children nodes, I thought that by creating an array of objects that adhere to NodeInterface should be enough but I might be wrong.
If anyone could point me to the right direction, that would be great.
Handi
More info:
What I did was actually following the ExtJs 4 documentation so I believe this is a bug on ExtJs 4 framework.
I also tested this with Extjs-4.1.0-rc3 and it also produced the same error.
I have resolved this issue changing the event itemexpand with the event afteritemexpand. I have no documentation to justify this. The debugger says to me that the view listen for the itemexpand event and do something that goes wrong if add a node manually...
For the question number 2: you have no raw object because it is created by the reader of the store after the load operation. So if you add a node by hand the raw object does not exists.
http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Model-property-raw
The documentation for .appendChild() functions says:
node : Ext.data.NodeInterface/Ext.data.NodeInterface[]
The node or Array of nodes to append
And you are trying to append an array of 2 objects, but they are not "NodeInterface" instances. So you should create them properly like so:
var nodeToAppend1 = pThis.createNode({text:"Teachers", type:"ChildStaff", leaf: false});
pThis.appendChild(nodeToAppend1);
I remember making the same mistake a year ago, so I hope this helps.
I think it happens because your Ext.TreePanel working asyncroniously. So add process is:
Append Child, add virtual child leaf to your tree, with red mark in corner
Call store's create api
Receive response, if it success=true, change with virtual
The goal of changing is set internakId property, so you can add new child only after this change happens. I think better is write some recursive function and bind it to store's added event
tree.getStore().on('update', addChild);
or
tree.getStore().load({
success: function() {
addChild()
});