Properties dynamisch erzeugen, in C++

Beispielmodul: hellodyn.

loadOstPropertiesFromFile() ist nur eine Komfortfunktion: Sie ruft für jede Property und jedes Element der JSON-Datei genau dieselben Primitiven auf, die ein Modul selbst jederzeit während seiner Lebensdauer direkt aufrufen kann — bei der Instanziierung oder auch später, als Reaktion auf eine Benutzeraktion.

Eine Property manuell aufbauen

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);
level1/level2 in C++, devcat/group in JSON

Der Konstruktor von PropertyMulti nennt seine beiden Hierarchie-Parameter level1/level2 (und das ist auch der Name, der dem Client im JSON-Dump zurückgegeben wird). Aber der JSON-Schlüssel einer .json-Datei eines Moduls ist devcat/group, nicht level1/level2 — zwei unterschiedliche Namen für dieselbe Sache, je nachdem, ob man C++ oder JSON schreibt. Eine leicht zu machende Verwechslung.

Jeder Elementtyp hat dasselbe Konstruktor-Schema: ElementXxx(clé, libellé, ordre, info-bulle). addElt() muss nach createProperty() aufgerufen werden.

Für ElementInt/ElementFloat werden die numerischen Beschränkungen (min/max/step/format — dieselben Schlüssel wie in JSON, siehe Der Typenkatalog) nach der Konstruktion über eigene Setter festgelegt:

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);

Das letzte Argument (hier false) ist dasselbe emitEvent wie bei setValue() — es lohnt sich nicht, den Client über eine Metadatenänderung zu informieren, wenn das Element auf der Frontend-Seite noch gar nicht existiert.

Eine Property auf Anfrage erzeugen, nicht beim Start

Ein Button (ElementBool, directedit:true) kann die Erzeugung auslösen, statt sie beim Start durchzuführen:

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);
    }
}

Jeder Zweig setzt seinen eigenen Button nach der Verarbeitung der Aktion wieder auf false zurück — ein Aktions-Button ist ein einmaliger Auslöser, kein dauerhafter Schalter.

Ein Element zu einer bereits existierenden Property hinzufügen oder entfernen

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() benachrichtigen den Client über die Änderung (ein neues Element erscheint oder verschwindet, ohne dass ein Neuladen der Seite nötig ist).

Den Überblick über hinzugefügte Schlüssel behalten

PropertyMulti nummeriert seine Elemente nicht selbst — es ist Aufgabe des Moduls, eindeutige Schlüssel zu erzeugen (zum Beispiel einen Zähler), um zu wissen, welche existieren und in welcher Reihenfolge sie entfernt werden sollen.