projectstorm / react-diagrams

a super simple, no-nonsense diagramming library written in react that just works
https://projectstorm.cloud/react-diagrams
MIT License
8.69k stars 1.17k forks source link

Typed Serialization #744

Closed archaeron closed 3 years ago

archaeron commented 4 years ago

right now if I'm not mistaken the only way to add custom data to a deserialized node is to stick it into the extras: any bit.

It would be really nice if there was a typed way of doing that.

The best way to do it would probably be to add some generic type parameters to DiagramModel to specify the NodeModels and the LinkModels

Is there another way that I'm missing?

Thnka you :)

sinanyaman95 commented 4 years ago

Couldn't understand what you mean by "typed way" of doing that. Can you elaborate on that?

In the DiagramModel serialized json object you have a 'layers' key which is a 2-element array. The first one will be for links, the other will be for nodes. Inside the 'models' key of those objects you can see an array of links and nodes.

Not sure if that is what you meant, but will be happy to try to help if not.

archaeron commented 4 years ago

hi @sinanyaman95 thanks for the reply

let's say I have two different kinds of nodes with one an input field and the other a drop down. when I serialize the two nodes I'd like to have the values of the input fields in the serialized values.

but since the return type of serialize is fixed, the only place I can put those values is in the extras: any which means I lose all type information

sinanyaman95 commented 4 years ago

I see, yeah as far as I know extras: any is the only way to store additional information on the nodes. I store my extra information as a json object on that field.

You can implement a custom Node Model which will extend the NodeModel and you can define the serialize() function by calling super.serialize() and adding another field to it like return { ...super.serialize(), inputField: this.inputFiled }. I don't know if there is an easier way of doing this.

archaeron commented 4 years ago

putting it in extras or doing it the other way means you have to cast to use it though :(

thanks for your help :)

sinanyaman95 commented 4 years ago

Yeah, you are right. node.getOptions().extras = {'inputField' : 'test'} is the way I use. Hope you will work around that, happy coding

dylanvorster commented 3 years ago

@archaeron the correct way to do this is to implement a custom node with a custom serialize and deserialize method. You will then not need to use generics. Please take a look at how the DefaultNode and then create your own node similar to the custom node demo

archaeron commented 3 years ago

@dylanvorster with a custom serialize and deserialize, do you mean under a new name? because the serialize in DefaultNodeModel is even "typed" as any. I wouldn't exactly call that typesafe :)

dylanvorster commented 3 years ago

Sigh. I also wouldn't call that typesafe but its also not what Im suggesting you should do and its also not the point. I am suggesting that you create your own new node that implements your own serialize and deserialize methods, which you can then type to your hearts content. The default node is supposed to show how it is possible at a conceptual level to extend the library and how to get up and running quickly. I purposefully typed it as any because in many cases you shouldn't have to care about the type of a serialised graph since most people are going to store it as a blob in DB. The default node cannot accommodate everyone, and it's also not supposed to. Successful implementations of the library such as the excellent work by @renato-bohler demonstrate this very well.

archaeron commented 3 years ago

@dylanvorster sorry, I didn't take enough time to answer and I'm sorry for that.

you are right, I can create my own node. With my own serialize function with it's own return type. What I don't understand yet, is how I get all nodes serialized then.

If I do:

engine.getModel().serialize()

I won't get that type, I will get the one from CanvasModel.serialize which I could only change If I made a sublass of that as well.

Or if I do

const models = engine
              .getModel()
              .getNodes()
              .map((model) => {
                const serialized = model.serialize();
                return serialized;
              });

I will get the type of NodeModel.serialize which I can also not change.

Could you point me to @renato-bohler 's solution? I can't find it.

thanks for your help and the great library!