Creating properties dynamically, in C++
Example module: hellodyn.
loadOstPropertiesFromFile() is only a convenience: for each property and each element in the JSON, it calls exactly the same primitives that a module can call directly itself, at any point in its lifetime — at instantiation, or later, in reaction to a user action.
Building a property by hand
OST::PropertyMulti *pm = new OST::PropertyMulti(
"hello", "Hello", OST::Permission::ReadOnly,
/* level1 */ "", /* level2 */ "", /* order */ "1",
/* hasProfile */ false, /* hasGrid */ false);
createProperty(pm);
OST::ElementString *message = new OST::ElementString("message", "Message", "1", "");
message->setValue("Hello, world!");
pm->addElt(message);PropertyMulti’s constructor names its two hierarchy parameters level1/level2 (and that’s also the name sent back to the client in the JSON dump). But the JSON key in a module’s .json file is devcat/group, not level1/level2 — two different names for the same thing, depending on whether you’re writing C++ or JSON. An easy mix-up to make.
Every element type has the same constructor signature: ElementXxx(key, label, order, tooltip). addElt() must be called after createProperty().
For ElementInt/ElementFloat, the numeric constraints (min/max/step/format — the same keys as in JSON, see The type catalog) are set after construction, through dedicated setters:
OST::ElementFloat *step = new OST::ElementFloat("step", "Step size", "2", "");
step->setMinMax(1.0, 1000.0, false);
step->setStep(1.0, false);
step->setFormat("%.0f", false);
pm->addElt(step);The last argument (false here) is the same emitEvent as setValue() — no point notifying the client of a metadata change at a time when the element doesn’t exist yet on the frontend.
Creating a property on demand, not at startup
A button (ElementBool, directedit:true) can trigger the creation instead of doing it at startup:
void HelloDyn::onExternalEvent(OST::ExtEvent event)
{
if (event.ev != OST::ExtEvType::SV || event.prpkey != "actions") return;
if (event.eltkey == "create")
{
if (!getStore().contains("hello"))
{
OST::PropertyMulti *pm = new OST::PropertyMulti("hello", "Hello",
OST::Permission::ReadOnly, "", "", "2", false, false);
createProperty(pm);
OST::ElementString *message = new OST::ElementString("message", "Message", "1", "");
message->setValue("Hello, world! (créé à la demande)");
pm->addElt(message);
}
getEltBool("actions", "create")->setValue(false, true);
}
}Each branch resets its own button to false once the action has been handled — an action button is a one-shot trigger, not a persistent toggle.
Adding or removing an element from an existing property
if (event.eltkey == "addfield")
{
OST::ElementString *field = new OST::ElementString(key, label, "", "");
field->setValue(value);
getProperty("hello")->addElt(field);
}
if (event.eltkey == "removefield")
{
getProperty("hello")->deleteElt(key);
}addElt()/deleteElt() notify the client of the change (a new element appears, or disappears, without needing a page reload).
PropertyMulti doesn’t number its elements itself — it’s up to the module to generate unique keys (a counter, for example) to know which ones exist and in what order to remove them.