Model attributes
Shapes contain several model attributes, common for both elements and links. These attributes work using mvc.Model
attributes functionality. Elements and links provide a subset of model attributes, which is specific to their purpose. You can find description of these attributes in the correspondent Element and Link sections of the documentation.
Working with model attributesβ
To work with the model attributes we recommend using prop()
and set()
methods. These methods will help you store any data on the model, while at the same time also providing a nice separation between model and presentation attributes.
You can store any custom data inside the shape using model attributes. However, it is recommended to use the data
attribute for this purpose. This attribute is reserved for custom data and is not used by JointJS for any other purpose.
The prop() methodβ
The prop()
method is used to set attributes on the shape model. It can be used to set both model and presentation attributes, and it also provides support for nesting making it very flexible. When setting an attribute, the first parameter is an object or string representation of our path, and when not using an object, the second parameter will be the value we wish to set. Prop will merge the properties you want to set with existing ones already present in the shape model.
element.prop('size/width', 100); // Set element attribute
element.prop('attrs/body/stroke', '#FFF'); // Set presentation attribute
element.prop('data', 10); // Set custom attribute with string
element.prop({ data: 10 }); // Set custom attribute with object
// Output from element.toJSON();
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 100, "height": 1 }, // Element attribute
"angle": 0,
"id": "19cf14e6-cb78-4c32-b9a1-4256dc53776f",
"data": 10, // Our custom attribute
"attrs": {
"body": {
"stroke": "#FFF" // Our presentation attribute
}
}
}
element.prop('data/count', 10); // Set nested custom attribute with string
element.prop({ data: { count: 10 }}); // Set nested custom attribute with object
element.prop('data': { count: 10 }); // This also creates a nested custom attribute
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "19cf14e6-cb78-4c32-b9a1-4256dc53776f",
"data": {
"count": 10 // Our nested custom attribute
},
"attrs": {}
}
prop()
not only provides support for nested objects, but for nested arrays too.
element.prop('mylist/0/data/0/value', 50); // Set custom attribute as nested array
// Output from element.toJSON();
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "9adab5e5-cebe-419f-8d62-39cce5486d0d",
"mylist": [
{
"data": [
{
"value": 50 // Our nested custom attribute
}
]
}
],
"attrs": {}
}
You can pass { isolate: true }
if the property change does not affect the connected links. Typically, changing the color has zero effect on attached links. By default, the cell and all connected links are updated.
cell.attr(['line', 'stroke'], 'red', { isolate: true });
The set() methodβ
The set()
is a method provided by mvc.Model, and similarly to prop()
, it can be used to set any attributes on the shape model. Like prop()
, when setting an attribute, the first parameter can be an object or string, but set()
doesn't provide nesting capability in the form of a string. That means any path representation is considered to be one attribute. Again, when not using an object, the second parameter is the value we wish to set. Another difference to take note of is that set()
will override attributes, while prop()
merges them.
element.set('size', { width: 100, height: 50 }); // Set element attribute
element.set('data', 10); // Set custom attribute with string
element.set({ data: 10 }); // Set custom attribute with object
// Output from element.toJSON();
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 100, "height": 50 },
"angle": 0,
"id": "c0a6696d-1857-4fb7-892d-409433f84d29",
"data": 10, // Our custom attribute
"attrs": {}
}
element.set('data/count', 10); // We try to set a nested custom property using set()
// The output produced will not be nested as is the case when using prop()
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "afdc42ce-5b10-45e5-832e-afcf2a221314",
"data/count": 10, // Note the important difference here
"attrs": {}
}
Overwriting attributes with prop()β
We do provide some extra functionality when using prop, and that is to enable rewrite mode. To enable rewrite mode, we simply use { rewrite: true }
as the 3rd argument in our prop()
method. This will replace the value referenced by the path with the new one. This differs from the default behavior which is to merge our properties.
element.prop('custom/state/isCollapsed', true);
element.prop('custom/state', { isActive: false });
// Output from element.toJSON();
// We can see our attributes have been merged
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "b1c02090-e46a-4d90-a5dc-5096f1559b9f",
"custom": {
"state": {
"isCollapsed": true,
"isActive": false
}
},
"attrs": {}
}
element.prop('custom/state/isCollapsed', true);
element.prop('custom/state', { isActive: false }, { rewrite: true });
// We can see our attributes have been overwritten
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "b1c02090-e46a-4d90-a5dc-5096f1559b9f",
"custom": {
"state": {
"isActive": false
}
},
"attrs": {}
}
Relationship between prop() and attr() methodsβ
Both of these methods function similarly, but there are a few small differences to be aware of. Internally, attr()
implements prop()
to process our attributes. Afterwards, the method places the presentation attributes within the attrs
object. Separating attributes in this manner also provides our model with a nice semantic and organizational divide between our model and presentation properties.
In the following example, you can see both attr()
and prop()
in action. It would be possible to set both of these attributes using prop()
, but as mentioned above, both these methods achieve what we want in our example. We see that nice separation between attributes, because after attr()
implements prop()
, it also prepends our path with 'attrs'
. This means we find our presentation attributes in the attrs
object.
element.attr('body/strokeWidth', 2);
element.prop('isCollapsed', true);
// Output from element.toJSON();
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "edafa2ac-27e6-4fbc-951a-aa3f9512c741",
"isCollapsed": true,
"attrs": {
"body": {
"strokeWidth": 2
}
}
}
Another important note to mention when talking about prop()
and attr()
is that when changing the model, some useful information is passed along with the change event in JointJS. propertyPath
, propertyValue
, and propertyPathArray
are all values which can be accessed when updating the model. This can prove useful if for some reason you need to listen to a specific attribute change. Note that it is not possible to access these values in this manner when using set()
.
graph.on('change', (cell, opt) => {
if ('attrs' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['attrs', 'body', 'fill'] 'was changed'
}
if ('isInteractive' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['isInteractive'] 'was changed'
}
});
element.attr('body/fill', 'cornflowerblue');
element.prop('isInteractive', true);
Attributesβ
Here is the list of the attributes presented in all shapes.
Typeβ
Every shape has its own type attribute which JointJS needs to find a corresponding view to properly render shapes. Also, this type is used to get the correct class to create shapes when using serialization. More information on a shape's type can be found in the cell namespaces section.
Presentation attributesβ
The presentation attributes are stored in a special attrs
attribute. The keys of the attrs
object are selectors that match subelements defined in the element's markup
. The values of this object are SVG attributes that will be set on the selected subelements. One can find the full list of SVG attributes and their descriptions online, e.g. on MDN.
For example, in order to set a red fill color on a subelement called 'body'
, the attrs
object would contain:
body: { fill: 'red' }
In the current documentation, we are separating model attributes, which are part of our model-view architecture and presentation attributes, which are SVG attributes that can be applied to the DOM markup.
If you simply need to change a value of an SVG attribute, it is not recommended to modify the attrs
object directly. Instead, use the attr()
method.
element.attr('body/fill', 'red');
We can use the shapes.standard.Rectangle
element (which inherits from dia.Element
) as an example. The attrs
object in its definition is provided below:
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
strokeWidth: 2,
stroke: '#000000',
fill: '#FFFFFF'
},
label: {
textVerticalAnchor: 'middle',
textAnchor: 'middle',
x: 'calc(0.5*w)',
y: 'calc(0.5*h)',
fontSize: 14,
fill: '#333333'
}
}
Notice that the object makes use of special JointJS attributes (e.g. textVerticalAnchor
) on top of standard SVG attributes (e.g. stroke
, fill
). All of these special attributes are listed in the attributes section of this documentation. You should also refer to the section on special presentation attributes.
Styling using CSSβ
In addition to styling via attributes, you can apply styles to SVG elements using css. You can look at the full list of presentation attributes here.
Our shapes.standard.Rectangle
element has its type specified as standard.Element
, so its CSS class would be joint-type-standard-rectangle
. Using this class and selector attributes you can specify any CSS styles you want. For example, let's override the default (move
) cursor for standard rectangle elements:
.joint-type-standard-rectangle [joint-selector="body"] {
cursor: pointer
}
.joint-type-standard-rectangle [joint-selector="label"] {
cursor: text
}
Zβ
The z
attribute specifies the stack order of the shape in the SVG DOM. A shape with a higher z
value will be rendered in front of a shape with a lower z
value.