Creare proprietà dinamicamente, in C++

Modulo di esempio: hellodyn.

loadOstPropertiesFromFile() è solo una comodità: chiama, per ogni proprietà e ogni elemento del JSON, esattamente le stesse primitive che un modulo può chiamare da solo direttamente, in qualsiasi momento della sua vita — all’istanziazione, o anche più tardi, in reazione a un’azione utente.

Costruire una proprietà a mano

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

Il costruttore di PropertyMulti denomina i suoi due parametri di gerarchia level1/level2 (ed è anche il nome restituito al client nel dump JSON). Ma la chiave JSON di un file .json di modulo è devcat/group, non level1/level2 — due nomi diversi per la stessa cosa, secondo che si scriva C++ o JSON. Una confusione facile da fare.

Ogni tipo di elemento ha lo stesso schema di costruttore: ElementXxx(chiave, etichetta, ordine, tooltip). addElt() deve essere chiamato dopo createProperty().

Per ElementInt/ElementFloat, i vincoli numerici (min/max/step/format — stesse chiavi del JSON, vedi Il catalogo dei tipi) si impostano dopo la costruzione, tramite setter dedicati:

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

L’ultimo argomento (false qui) è lo stesso emitEvent di setValue() — non vale la pena avvisare il client di un cambio di metadato nel momento in cui l’elemento non esiste ancora lato frontend.

Creare una proprietà su richiesta, non all’avvio

Un pulsante (ElementBool, directedit:true) può attivare la creazione invece di farla all’avvio:

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

Ogni ramo riporta il proprio pulsante a false una volta trattata l’azione — un pulsante di azione è un innesco puntuale, non un interruttore persistente.

Aggiungere o rimuovere un elemento da una proprietà già esistente

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() notificano al client la modifica (un nuovo elemento appare, o scompare, senza che sia necessario un ricaricamento della pagina).

Tenere traccia delle chiavi aggiunte

PropertyMulti non numera da sola i propri elementi — è compito del modulo generare chiavi univoche (un contatore, per esempio) per sapere quali esistono e in quale ordine rimuoverle.