Custom-meta-model: how can I 'write' an 'isMany' property


#1

Hi there,

I’m using the custom-meta-model to store our own Manufacturing-based properties.
Able to read/write the properties as long as it’s not a ‘isMany’ property. Also able to read isMany properties, but not able to write, I’m missing the correct syntax here, had a look at the examples, which are very helpfull, but no example where Klaus’ or Walter’s comments are added to the model.

I’ll try to provide as much info as possible.
For TLDR; Let me start what I use to write to the model, here’s the JS I use to ‘write’ my properties to the model:

    var manufacturing = moddle.create('mdm:ManufacturingDetails');
    businessObject.extensionElements = moddle.create('bpmn:ExtensionElements');
    businessObject.extensionElements.get('values').push(manufacturing);
    manufacturing.OS_HierarchyScope = OS_HierarchyScope.value;
    manufacturing.OS_Duration = OS_Duration.value;
    manufacturing.OS_DurationUOM = OS_DurationUOM.value;
    manufacturing.OS_OperationsType = OS_OperationsType.value;
    manufacturing.OS_EXT_WorkDefinitionID = OS_EXT_WorkDefinitionID.value;

Further explanation:
The functionality resembles somehow that of the properties panel.
In my case, it’ll slide in from the right and displays the properties if the user clicks a specific type of task.
To do this I’m using the event ‘element.click’ which after checking if the correct type of task is clicked fires this JS:

     // initiate the Extra data elements element = task object user clicked on, businessobjects = extra meta data model
            element = e.element;
            businessObject = element.businessObject;


            // move info into MES panel
            document.getElementById("disp_id").value = businessObject.name;


            // check if extensionElements already exist, if not create!  (in case of newly created segment)
            if (!businessObject.extensionElements) {                                // (typeof businessObject.extensionElements === 'undefined')

                var manufacturing = moddle.create('mdm:ManufacturingDetails');
                businessObject.extensionElements = moddle.create('bpmn:ExtensionElements');
                businessObject.extensionElements.get('values').push(manufacturing);
                document.getElementById("OS_HierarchyScope").value = "";
                document.getElementById("OS_Duration").value = "";
                document.getElementById("OS_DurationUOM").value = "";
                document.getElementById("OS_OperationsType").value = "";
                document.getElementById("OS_EXT_WorkDefinitionID").value = "";

            }
            else {



                // todo, is this the correct method to read the extensionElements values? see below
                var eE = businessObject.extensionElements.values;
                var data = eE[0];


                document.getElementById("OS_HierarchyScope").value = data['OS_HierarchyScope'];
                document.getElementById("OS_Duration").value = data['OS_Duration'];
                document.getElementById("OS_DurationUOM").value = data['OS_DurationUOM'];
                document.getElementById("OS_OperationsType").value = data['OS_OperationsType'];
                document.getElementById("OS_EXT_WorkDefinitionID").value = data['OS_EXT_WorkDefinitionID'];

                // check if parameters exist for this segment.
                if (typeof data['OS_Parameters'] !== 'undefined')
                {
                    var OS_Parameters = data['OS_Parameters'];
                    for (i = 0; i < OS_Parameters.length; i++) {
                        console.log(OS_Parameters[i].param);
                        console.log(OS_Parameters[i].value);
                    }
                }
                

            }

As you might have noticed, I use a somewhat different way to read the info from the model as in the example, this is due to errors on .filter() I received in the getExtension function in the example. But the solution above works out perfectly well and the properties/attributes are all accessible by name. Also, besides ready the ‘isMany’ ‘OS_Parameters’ property I’m not yet doing anything with it, just wanted to first see if I can read/write it.

Sidenote: the fact that .filter() probably errors out is that I haven’t build a bpmn-js my own with the custom-meta-model in it, because I just don’t understand all this node.js yet, see other thread about installing properties-panel.

So I solved the require-part in the example where the json-xml-schema-file is called as follows:

var _extensionDefinition = JSON.parse('with a very long string containing the definition, see below...');


var canvas = $('#canvas');

// modeler instance
var bpmnModeler = new BpmnJS({
    container: canvas,
    moddleExtensions: {
        mdm: _extensionDefinition
    }
});


And just used the default seed you supply, all works out perfectly fine, only that ‘isMany’ writing of the property

json definition of my ManufacturingDataMode
{
  "name": "ManufacturingDataModel",
  "uri": "http://some-company/schema/bpmn/mdm",
  "prefix": "mdm",
  "xml": {
    "tagAlias": "lowerCase"
  },
  "types": [
    {
      "name": "ManufacturingDetails",
      "superClass": [ "Element" ],
      "properties": [
        {
          "name": "OS_HierarchyScope",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "OS_Duration",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "OS_DurationUOM",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "OS_OperationsType",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "OS_EXT_WorkDefinitionID",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "OS_Parameters",
          "isMany": true,
          "type": "Parameter"
        }
      ]
    },
    {
      "name": "Parameter",
      "properties": [
        {
          "name": "param",
          "isAttr": true,
          "type": "String"
        },
        {
          "name": "value",
          "isBody": true,
          "type": "String"
        }
      ]
    }
  ],
  "emumerations": [],
  "associations": []
}

So, as you might have noticed, I’m pretty inexperienced with these type of projects, but I’m missing only one small syntactical thingy here, most likely an index that I set wrong, tried a lot of things, but don’t know where to look further.

Thanks for your patience!


#2

In the modeler we use a commandStack to implement do and undo. Do you intend to support this?

Regarding writing isMany properties, these are exposed as plain JavaScript Array elements. So you may safely interact with them using the standard API for array manipulation exposed in JavaScript:

var manufacturingDetails = bo.extensionElements.values[0];

var parameters = manufactoringDetails.get('OS_Parameters');

// remove first parameter, in place
parameters.splice(1, 1);

// add a parameter, in place
parameters.push(newParameter);

Hope this helps!


#3

Hi @nikku, Thanks for your reply.

regarding commandStack, I haven’t thought about that yet, but I think it’s good functionality to have.

Then, my issue, I made some progress in that I am now able to write ‘many’ parameters to the model which I then am also able to read and see the affected changed values. So far so good,

But, when I then export the xml, I get an ‘undefined’… So I think that the modeler doesn’t exactly do what I’d like to, but weird that I’m still able to read out the values using javascript.

Here’s my JS to write the parameters.

    var newParam = {
        "$type": "mdm:Parameter",
        "param": "SerialNumber",
        "value": "01346876845"
    };

    var manufacturingDetails = businessObject.extensionElements.values[0];
    var parameters = manufacturingDetails.get('OS_Parameters');
    parameters.splice(1, 1);
    parameters.push(newParam);

    newParam = {
        "$type": "mdm:Parameter",
        "param": "Tested",
        "value": "failed"
    };
    parameters.splice(2, 1);
    parameters.push(newParam);

Any idea’s?


#4

We use bpmn-moddle under the hood to build the object graph and export to XML. Because of that using plain objects as elements won’t work. Instead, instantiate your types via Moddle#create:

var moddle = bpmnJS.get('moddle');

var newParam = moddle.create('mdm:Parameter', {
  "param": "SerialNumber",
  "value": "01346876845"
});

#5

@nikku, thanks for your reply, works like a charm!
Able to read/write the isMany with JS and valid XML pops out.
All nicely wrapped around your default seed-project without any npm installation needed.

Actually, this was the last bit of functionality I needed to get a proof of concept to convince my management that it’s way better to invest in hiring a good JS developer (as you might have noticed, I’m not, but you can ask me anything about MS SQL) than forking out a ton of money for a modeler which doesn’t even come close to what the default bpmn.io can do.

So, thanks, I’m definetely sure we’ll come back with a more experienced developer :wink:


#6

Thanks for sharing this success story.


#7

@nikku Thanks to you and your support.
Convincing management today took less than 15 minutes, decision to hire someone has already been made.

So in the meantime until we hire someone I’m going to roughly add/describe all the funcitonality we need and he/she can professionlize it and teach me to properly code JS. benefit = Knowledge shared.