What are all the GoJS NodeData and LinkData property fields? - javascript

So I'm wanting to dynamically build a GraphLinksModel in GoJS using addNodeData(nodedata) and addLinkData(linkdata). My issue is that I don't know what the fields of nodedata and linkdata are, other than the fields that are in example code. I can build the Node object and Link object for each of my nodes and links, but addNodeData and addLinkData don't actually want those, they want "nodedata" and "linkdata". When I try to addNodeData(Node) then I get an error.
Here are the descriptions of addNodeData(nodedata) and addLinkData(linkdata). As you can see, it just defines the parameter as an Object, with no hints, other that in the examples about what's supposed to be in it. Any direction or explanation would be appreciated.

Node data is a JavaScript Object with minimum of the below formats. There can be additional property added to it based on type of object that you use.
{ "id": 1, "text": "TextToDisplay1" }
{ "id": 2, "text": "TextToDisplay2" }
The link data is again a JavaScript Object with the below format
{ "from": 1, "to": 2, "text": "Link above 1 and 2", "curviness": 20 }
'From' attribute of this object will have the From node's Id value and the same is teh case with 'to' attribute, it is 'to' nodes id.

There are no "all", the Node data can contain anything, arbitrarily, but must contain a key. If no key is defined, the GoJS model will automatically assign one. In other words, if you add { } as your node data and look at it, you'll find it is:
{ __gohashid: 1975, key: -5 }
__gohashid is internal to GoJS and should not be modified or used. They key is unique.
You can change the uniqueness function of the model, and you can even change the keyword for key.
You can read more in the Model overview in the API.
Other than there are a few built in data properties:
category, which specifies which Node or Link template to use, but the default is "" and its not necessary to specify.
In TreeModel there's also parent, which specifies the key of the parent Node.
Any other properties you find on the Model don't mean anything unless the Diagram's Node and Link templates have data-bindings to those names. For example this template has two data bindings:
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto", // the Shape will go around the TextBlock
$(go.Shape, "RoundedRectangle", { strokeWidth: 0},
// Shape.fill is bound to Node.data.color
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 }, // some room around the text
// TextBlock.text is bound to Node.data.key
new go.Binding("text", "key"))
);
If color exists in the Node data, it will be used as the Shape.fill in this Node template's Shape.
If key exists (it has to) in the Node data, it will be used as the TextBlock.text.
You can see that in action here: http://codepen.io/simonsarris/pen/ORwoLA?editors=1010
Let me know if you think there ought to be additional clarifications and I'll update the answer.

Related

How to implement tree nodes toggling in Vega JS?

I'm using Vega JS for building a tree chart. In general, my question is the following:
Vega documentation has a great example of tree layout. How can I extend it with an ability to collapse & expand its nodes?
To be more specific, let's consider an example of tree chart that I'm building here in Vega Editor.
If you click on the nodes, they will toggle (expand or collapse), allowing you to see particular branches of the tree. This feature works fine unless you try to collapse the top-level node (region) while keeping the second-level nodes (districts) expanded. In that case the tree will look like this:
It happens because of the way I handle this interaction:
When you click on a node, toggledNode signal is triggered, which in turn triggers toggle action in the expandedNodes data array. I.e. by clicking on a node, I add or remove that node to the expandedNodes array (more precisely, we add/remove a reduced object with only name property)
Thus expandedNodes data contains information about which nodes are explicitly expanded. But it doesn't know if those expanded nodes are inside of a collapsed parent node.
Then, to find out which nodes are actually visible, I use visibleNodes data. There I apply filter transform with the following expression: !datum.parent || indata('expandedNodes', 'name', datum.parent). I.e. I check only one level up: if the node's parent is present in the expandedNodes array , I consider the node as visible.
The problem is the following: I can't find any way of extending this functionality across multiple levels.
Probably I could write some hooks to check the same condition across 2 or 3 levels, e.g:
!datum.parent ||
indata('expandedNodes', 'name', datum.parent) &&
indata('expandedNodes', 'name', datum.myCustomFieldWithParentNode.parent) &&
indata('expandedNodes', 'name', datum.myCustomFieldWithParentNode.myCustomFieldWithParentNode.parent)
But it seems too complex for such a simple problem, and also it's not a final solution. In theory, a tree may contain dozens of nesting levels: what to do then?
I found one useful expression in Vega: treeAncestors. I could easily write a solution in JavaScript, where I have loops and array methods such as .some() and .every(). But apparently Vega doesn't support any expressions to iterate over an array. So even though I can get an array of tree node ancestors with treeAncestors function, I can't do anything with it to verify that all ancestors are expanded.
Either my approach is wrong, and somebody can find a better algorithm for doing the same, which doesn't require iterating over arrays (except for data and indata expressions) - or it's a current limitation of Vega.
You can use treeAncestors and then use a flatten transform to get a dataset that you can query. In your case it would look something like:
{
"transform": [
{
"as": "treeAncestors",
"type": "formula",
"expr": "treeAncestors('tree', datum.id, 'root')"
}
],
"name": "tree-ancestors",
"source": "tree"
},
{
"transform": [{"fields": ["treeAncestors"], "type": "flatten"}],
"name": "tree-ancestors-flatt",
"source": "tree-ancestors"
},
{
"transform": [
{
"type": "filter",
"expr": "indata('selected', 'value', datum.treeAncestors.id)"
}
],
"name": "filtered",
"source": "tree-ancestors-flatt"
},
{
"transform": [{"type": "aggregate", "groupby": ["id"]}],
"name": "filtered-aggregate",
"source": "filtered"
},
{
"transform": [
{
"type": "filter",
"expr": "indata('filtered-aggregate', 'id', datum.id) "
}
],
"name": "filtered-tree",
"source": "tree"
}
Vega doesn't seem to have a recursive way of solving the problem for your question" hey, if all my parents are expanded, then I am visible as a node ".
You can check indeed conditions for all levels you wish to define.
{
"type": "filter",
"expr": "!datum.parent || indata('expandedNodes','name',datum.parent)&&datum.depth==1||(indata('expandedNodes','name',datum.firstParent)&&indata('expandedNodes','name',datum.secondParent)&&datum.depth==2)||(indata('expandedNodes','name',datum.firstParent)&&indata('expandedNodes','name',datum.secondParent)&&indata('expandedNodes','name',datum.thirdParent)&&datum.depth==3)"
}
The code above says to VEGA : " hey check if all of my defined parents are expanded and filter me if any of my parents exist but are not expanded
To see the full solution with your case, please check :
spec

Alpaca framework select with multiple:true inherits the selection status of its former siblings

This issue original raise on github of alpaca framework, https://github.com/gitana/alpaca/issues/731
Just open one here for getting support from stackoverflow community.
A Combination of multi select and array cause a problem that the second and later select boxes inherit the selection status of their former siblings.
See my codepen: https://codepen.io/hadakadenkyu/full/pooKyzy
any help would be appreciated!!
Your issue was related to the object datasource that you've created.
Internally alpacajs transforms your datasource object to text and value, and it needs it to be only of type {key: value} so in your example you should do {"2001": 2002} for example.
Moreover, in your alpaca form data object you should use select option values not text like: year: ["2001", "2003"]
Update:
I rechecked the documentation and I saw:
Datasource for generating list of options. This can be a string or a function. If a string, it is considered S be a URI to a service that produces a object containing key/value pairs or an array of elements of structure {'text': '', 'value': ''}. This can also be a function that is called to produce the same list.
So to make your example works you should wrap your datasource object value into a function like:
dataSource: function(callback) {
callback([
{ "text": "2000", "value": 2000 }, ...
Here's the first example.
Here's the second example.

Deleting an object from a nested array in DynamoDB - AWS JavaScript SDK

I'm building an app where I need to delete items stored in the database. Here's a (shortened) example of user data I have in my DynamoDB table called 'registeredUsers':
{
"userId": "f3a0f858-57b4-4420-81fa-1f0acdec979d"
"aboutMe": "My name is Mary, and I just love jigsaw puzzles! My favourite jigsaw category is Architecture, but I also like ones with plants in them.",
"age": 27,
"email": "mary_smith#gmail.com",
"favourites": {
"imageLibrary": [
{
"id": "71ff8060-fcf2-4523-98e5-f48127d7d88b",
"name": "bird.jpg",
"rating": 5,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/bird.jpg"
},
{
"id": "fea4fd2a-851b-411f-8dc2-1ae0e144188a",
"name": "porsche.jpg",
"rating": 3,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/porsche.jpg"
},
{
"id": "328b913f-b364-47df-929d-925676156e97",
"name": "rose.jpg",
"rating": 0,
"url": "https://s3.eu-west-2.amazonaws.com/jigsaw-image-library/image-library/images/rose.jpg"
}
]
}
}
I want to be able to delete the item 'rose.jpg' in the user.favourites.imageLibrary array. In order to select the correct user, I can provide the userId as the primary key. Then, in order to select the correct image in the array, I can pass the AWS.DocumentClient the 'id' of the item in order to delete it. However, I'm having trouble understanding the AWS API Reference docs. The examples given in the developer guide do not describe how to delete an item by looking at one of it's attributes. I know I have to provide an UpdateExpression and an ExpressionAttributeValues object. When I wanted to change a user setting, I found it pretty easy to do:
const params = {
TableName: REGISTERED_USERS_TABLE,
Key: { userId },
UpdateExpression: "set userPreferences.difficulty.showGridOverlay = :d",
ExpressionAttributeValues: {
":d": !showGridOverlay
},
ReturnValues: "UPDATED_NEW"
};
To conclude, I need a suitable Key, UpdateExpression and ExpressionAttributeValues object to access the rose.jpg item in the favourites array.
Unfortunately, the UpdateExpression syntax is not as powerful as you would have liked. It supports entire nested documents inside the item, but not sophisticated expressions to search in them or to modify them. The only ability it gives you inside a list is to access or modify its Nth element. For example:
REMOVE #favorites.#imagelibrary[3]
Will remove the 3rd element of imagelibrary (note that the "#imagelibrary" will need to be defined in ExpressionAttributeNames), and you can also have a condition on #favorites.#imagelibrary[3].#id, for example, in ConditionExpression. But unfortunately, there is no way to specify more complex combinations of conditions and updates, such as "find me the i where #favorites.#imagelibrary[i].#id is equal something, and then REMOVE this specific element".
Your remaining option is to read the full value of the item (or with ProjectionExpression just the #favorties.#imagelibrary array), and then in your own code find which of the elements you want to remove (e.g., discover that it is the 3rd element), and then in a separate update, remove the 3rd element.
Note that if there's a possibility that some other parallel operation also changes the item, you must use a conditional update (both UpdateExpression and ConditionExpression) for the element removal, to ensure the element that you are removing still has the id you expected. If the condition fails, you need to repeat the whole operation again - read the modified item again, find the element again, and try to remove it again. This is an example of the so-called "optimistic locking" technique which is often used with DynamoDB.

update nested value in javascript array

This is an odd question, but I hope someone can help me out.
I have a JS object (it's actually JSON string which is then converted back and object.).
{
"legs": [{
"pods": [{
"11": {
"id": 6,
"name": "tesla",
"text": "tesla model2",
"battery": "60"
},
"21": {
"id": 9,
"name": "porsche",
"text": "porsche electric",
"battery": "100"
}
}]
}]
}
So in my javascript I'll call a specific item... say, legs.pods[11].name
I pass that into a function like editThisField(legs.pods[11].name);
On that function editThisField(whichDataElement){.....} I'll show a modal box where the user can provide new text for the name value... what I would like is to have the whichDataElement updated to the new text such that which ever object variable I pass into that field, is updated in the legs object... I'm not sure if I'm explaining this very well, but I can't think where to start looking at a good approach to this.
Essentially I'm making a text area where I paste in the JSON, and the system allows me to edit the JSON object via modal popups. So the modal appears and dynamically shows a form based on the data in the specific sub object. When you fill in the form, the original JSON is updated... thoughts?
Not really working as I need... Maybe this helps clarify:
var mything = {
subthing: {
thisthing: "here"
}
}
var edit = mything.subthing.thisthing;
edit = "Changed";
console.log(mything); // then the listthing value should be changed.
It sounds like you're on the right track. If you pass in an object to your editThisField function you can change it's contents, but in your example you are passing in a primitive (which won't work as it's passed by value).
Another option... since you're loading a modal I assume you know what attribute you want to change so you can just specify the object keys or array indices dynamically. See example here https://jsfiddle.net/jroden1f/. Note, I changed your data model slightly as I wasn't sure why you had legs and pods as arrays and it made the example easier if I just made them objects.

ag-grid: hierarchical tree of referenced nodes: how to make the referenced attributes editable?

I am testing the awesome ag-Grid component (really cool!) for a new Angular2 project. It should be used to display a CAD model structure with a "structure" (aka "tree"), and each node being the attributes of a CAD model.
Now, it is well possible to make a tree in ag-Grid. In its simpliest form, the data property holds the properties of the node (CAD model attributes). In this case, I can easily make the cells editable by assigning the property editable: true to the columnDefs.
However, the problem is, that if a CAD model is used in many places of the structure (tree), and one is edited, all others do not change.
Instead, the CAD models (the nodes) should in fact be references to the data, not the data itself. Ok, no problem with ag-Grid. Here you can see the comparison between a tree with copied nodes and a tree with referenced nodes:
Nodes are copied in the tree
ag-Grid's rowData:
rowData = [
{
data: { modelName: 'partA', weight: 12.5 }
children: [ ... ]
}
];
The ag-Grid's column definition would be:
{
headerName: 'weight',
field: 'weight',
editable: true
}
Nodes are referenced in the treee
ag-Grid's rowData:
rowData = [
{
data: { model: MODEL_OBJECT }
children: [ ... ]
}
];
where MODEL_OBJECT is a javascript reference to the model object, which would be e.g.:
var modelA = { modelName: 'partA', weight: 12.5 }
And change the column definitions to:
{
headerName: 'weight',
valueGetter: 'data.weight',
editable: true
}
See? The same properties are shown in the tree, but in fact they are fetched from the referenced POJS object. Thus, the models will be shared among their usages in the tree.
All well so far. But now - since there is a valueGetter for the column, the column weight is not directly editable. To be more precise, the ag-Grid allows to edit it, but it does not modify the value. Well, this is understandable since the valueGetter is acting as a mapping function which may not be "reversible" in the other direction. Consider something like 'data.firstName + data.lastName': how in the world should ag-grid know how to update data.firstname or data.lastName if the user enters another value for the column.
My question: how can I achieve the goal of having editable cells of referenced tree nodes? The intended effect is that if I edit the properties of a CAD model in one place of the tree, then the grid updates all other occurrences automatically, through the references.
The inverse of valueGetter in ag-Grid is newValueHandler. It's explained here. You define the newValueHandler on the colDef which then gives you the freedom to do whatever you want in the value.
var colDef = {headerName: "Tree Value", valueGetter: "data.a+data.b", editable: true, newValueHandler: myNewValueHandler};
function myNewValueHandler(params) {
// set the value you want in here using the params
}

Categories