Liste beim Editieren dynamaisch ändern

Hallo,

ich spiele gerade ein Bisschen mit den Listen rum und komme bei dem, was ich vorhabe, nicht so recht weiter. Und zwar würde ich gerne während ich einen Listeneintrage hinzufüge oder editiere, eine Spalte dieses Eintrags dynamisch ändern.

Mal ein einfaches Beispiel:
Ich habe eine Liste mit zwei Spalten. Eine Spalte enthält eine Variablen-ID, die ich über „SelectVariable“ entsprechend auswählen kann. Die zweite Spalte soll den aktuellen Wert der ausgewählten Variable anzeigen. Das kann ich ja relativ einfach erreichen, wenn ich das Formular speichere. Ich möchte dies aber bereits direkt beim Hinzufügen oder Ändern eines Eintrags tun, ohne die Konfiguration erst speichern zu müssen. Geht das überhaupt?

Mein Ansatz war bisher für „onAdd“ und „onEdit“ ein Skript zu hinterlegen, bei dem ich UpdateFormField aufrufe, um die Spalten entsprechend zu aktualisieren. Aber irgendwie muss ich ja zunächst an alle Zeilen der (noch nicht gespeicherten) Liste ran kommen, um die Liste mit den geänderten Werten neu zusammenzubauen. Irgendwie fehlt mir da die Idee oder es ist einfach nicht möglich.

Der zweite Punkt, der in eine ähnliche Richtung geht, ist das dynamische Ändern des vorgegebenen Wertes beim Hinzufügen eines neuen Eintrags.

Auch hier ein einfaches Beispiel:
Die Liste enthält eine Spalte mit einem Index. Für jeden hinzugefügten Eintrag soll der Index automatisch um 1 erhöht werden. Auch hier wieder ohne die Instanzkonfiguration zuvor speichern zu müssen.

Da es viele Module gibt, die einen entsprechenden Index haben, diesen aber erst nach dem Speichern setzen, vermute ich, dass das anders einfach nicht möglich ist?!

Gruß
Slummi

  1. Das geht leider nicht, da die Spalten und deren onEdit Konfiguration statisch ist. Wir haben auch schon den Bedarf für mehr Dynamik in diesem Bereich bemerkt und auch schon Ideen dafür aber nichts auf der kurzfristigen Roadmap. (@Dr.Niels )

  2. Das kannst du mit UpdateFormField machen. Und dort dann den „add“ Wert jeweils anpassen, wenn onAdd aufgerufen wird. Damit kannst du die Art von ID-Generierung selbst bestimmen.

paresy

  1. sollte sehr wohl gehen. Wie du schon skizziert hast, machst du das über ein Skript in onAdd bzw. onEdit. Dort verwendest du die Variable $NameDerListe, welche alle Zeilen beinhaltet. Damit kannst du die neuen Werte bestimmen und per UpdateFormField in die Liste schieben.

  2. ebenfalls in onAdd, wie @paresy schon skizziert hat

Dann werde ich mich noch mal daran versuchen und gucken, wo mein Denkfehler war.

Ich glaube ich tue mich mit der aktuellen Zeile etwas schwer, die mich primär interessiert. Auf diese kann ich ja mit dem String-Index zugreifen. Um die Liste mit UpdateFormField zu aktualisieren, muss ich aber doch jede Zeile auslesen und die Liste neu zusammenbauen. Das kann ich über den numerischen Index tun, aber dann weiß ich ja nicht mehr, welches die aktuelle Zeile ist, oder?
Ich könnte vielleicht beide Varianten kombinieren und dann miteinander vergleichen. Aber theoretisch könnte es auch mehre identische Zeilen geben.

Ich probiere noch mal etwas rum.

Das stimmt, das klappt nicht direkt. Aber spricht etwas dagegen einfach via Holzhammer alle Zeilen zu aktualisieren?

1 „Gefällt mir“

Nein, eigentlich nicht. Aber irgendwie stehe ich gerade trotzdem auf dem Schlauch.

Ich kriege die Zeilen zwar ausgelesen, aber wie kriege ich sie wieder zusammengebaut, um sie an UpdateFormField übergeben zu können?

Muss ich da ein UpdateFormFiled('Tabellenname', 'values', json_encode($rows)); machen und in $rows muss ich dann sämtliche Zeilen mit den gewünschten Änderung als JSON-String neu übergeben?

Ganz genau so musst du es machen

Damit habe ich es jetzt prinzipiell erst mal hinbekommen. Allerdings bin ich noch über ein paar andere Punkte gestolpert:

  1. Ich hatte zunächst versucht die Liste mit einer for-Schleife zu iterieren, weil ich dachte, dass das IPSList-Objekt „Countable“ implementiert hat, was jedoch nicht der Fall ist. Mit foreach bin ich dann weiter gekommen. Macht es vielleicht Sinn „Countable“ noch zu implementieren?

  2. Wenn ich einem onDelete-Skript das IPSList-Objekt übergebe, enthält das Objekt beim Iterieren immer noch alle Zeilen inklusive der gelöschten Zeile. Ist das so richtig? Damit hebel ich nämlich im Zweifelsfall das Delete komplett aus und die Liste wird direkt wieder in ihren ursprünglichen Zustand (vor dem Delete) versetzt.

  3. UpdateFormField kann ich ja nur im Objekt-Kontext aufrufen. Das heißt ich kann es nicht direkt in onAdd etc. aufrufen und muss stattdessen eine öffentliche Modulfunktion bereitstellen, die ich dann aufrufen kann. Gibt es einen Trick, wie man das nutzen kann ohne eine öffentliche Modulfunktion bereitzustellen, die für den Anwender am Ende keinen Nutzen hat?

  4. Gibt es eine Möglichkeit ein Skript aufzurufen, nachdem man die Liste manuell per Drag&Drop umsortiert hat (changeOrder: true)? Ich dachte, dass ich das mit onClick für alle Spalten lösen kann. Aber auch wenn das Skript erst nach dem Umsortieren der Liste ausgeführt wird, enthält das IPSList-Objekt die Liste in der Reihenfolge vorm Umsortieren. Ich müsste also nach dem letzten Umsortieren noch mal in die Liste klicken, um das Objekt mit der korrekten Reihenfolge zu erhalten.

  5. Bei einer manuell sortierbaren Liste wird diese auch in der individuell gewünschten Sortierreihenfolge in der settings gespeichert und in dieser Reihenfolge wiederhergestellt. Kann man sich darauf verlassen, dass die individuelle Sortierreihenfolge auch zukünftig so gespeichert wird oder könnte sich das ändern?

Es geht bei der Liste in Sachen Dynamik doch mehr als ich dachte, aber einfach ist es nicht wirklich. Teilweise muss man sich für einen relativ kleinen Effekt ganz schön einen abbrechen, sodass es am Ende vermutlich oft einfacher ist darauf zu verzichten und manche Dinge erst nach einem Speichern der Einstellungen zu tun.

  1. Ja, das könnte man bestimmt machen, damit müsste ich mich mal auseinandersetzen.

  2. Ja, du erhälst die Liste vor dem Löschvorgang, damit du auch auf Basis der gelöschten Zeile agieren kannst. Ich verstehe aber gerade nicht, warum onDelete in deinem Anwendungsfall relevant ist. Du musst die Werte in den anderen Zeilen ja nicht aktualisieren.

  3. Korrekt, da muss eine passende Funktion aufgerufen werden. Welchen Trick ich hier in der Community schon das rein oder andere mal gesehen habe, ist ein Aufruf von IPS_RequestAction mit einem nicht existierenden Ident. Dann erstellst du keine neue öffentliche Funktion.

  4. Aktuell nicht, könnte man aber einbauen.

  5. Da die Sortierung ja Teil des Werts ist, soll diese natürlich auch in den Settings beibehalten werden.

Ich hänge mich hier mal dran…

Ich habe in einem Konfigurationsformular ein „Select“ ausserhalb einer Liste. Dort kann ich zwischen 3 Werten auswählen.

Jetzt soll in einer Liste je nach Wert des Selects bestimmte Spalten aus- bzw. eingeblendet werden. Dies klappt auch bereits mittels UpdateFormField, wenn ich aber z.B. eine Zeile in der Liste editieren will (Zahnrad) dann erscheinen ja die „ausgeblendeten“ Spalten ja wieder im „Listenelement bearbeiten“ Dialog.

Es müsste ja im Prinzip der Parameter „edit“ für dieses Listenelement leer belieben, bzw. nicht vorhanden sein, damit es auch in diesem Dialog nicht erscheint. Nur kann ich kann leider mit UpdateFormField „edit“ nicht ändern.

@Dr.Niels : Ist dies doch irgendwie abbildbar?

Ansonsten wäre meine Idee drei Listen mit den richtigen Spalten anzulegen und das über „visible“ zu steuern.

Uli

edit ist ja Teil der Spalten und müsste somit via UpdateFormField anpassbar sein. Wenn das nicht klappt, dann wäre das ein Bug. Magst du mal deinen Code zeigen, in dem du versuchst edit erfolglos neu zu setzen?

Hi Niels,

Danke für deinen Hinweis, ich schaue mir das heute Abend noch einmal an und komme wieder auf dich zu, wenn ich weitere Hilfe benötige.

Uli

@Dr.Niels

Ich erstelle das Konfigurationsformular dynamisch über

public function GetConfigurationForm()

 $acousticSignal1 = [
    "type" => "Select",
    "options" => [
        [
            "caption" => "Alarm Aus",
            "value" => 0
        ],
        [
            "caption" => "Extern Scharf",
            "value" => 1
        ],
        [
            "caption" => "Intern Scharf",
            "value" => 2
        ],
        [
            "caption" => "Alarm blockiert",
            "value" => 3
        ]
    ]
];


if ($selectValue == 'Test' ) {
    $acousticSignal1 = '';
}

 $form['elements'][] = [
            'type' => 'ExpansionPanel',
            'caption' => 'Auslöser',
            'items' => [
                [
                    'type' => 'List',
                    'name' => 'TriggerList',
                    'caption' => '',
                    'rowCount' => 5,
                    'add' => true,
                    'delete' => true,
                    'columns' => [
                        [
                            'name' => 'Use',
                            'caption' => 'Aktiviert',
                            'width' => '100px',
                            'add' => true,
                            'edit' => [
                                'type' => 'CheckBox'
                            ]
                        ],
                        [
                            'name' => 'Designation',
                            'caption' => 'Bezeichnung',
                            'width' => '300px',
                            'add' => '',
                            'edit' => [
                                'type' => 'ValidationTextBox'
                            ]
                        ],
                        [
                            'name' => 'AcousticSignal1',
                            'caption' => 'Akustisches Signal 1',
                            'width' => '200px',
                            'add' => 0,
                            'visible' => false,
                            'edit' => $acousticSignal1,
                        ]
                    ]
                ]
            ]
        ];

So, jetzt verstehe ich noch nicht so ganz, wie das UpdateFormField für die Spalte AcousticSignal1 anwenden kann.

Bei komplexen Parameter, also Listen oder Objekte, muss der neue Wert JSON-codiert übergeben werden.

$this->UpdateFormField(‚TriggerList‘, ‚columns‘, ?? value ??);

Muss ich für value wieder die komplette Liste als json angeben? Meine bisherigen Versuche waren leider nicht erfolgreich. Auch bei den SynconTest Module habe ich nichts gefunden.

Uli

Habe es glaub eich gefunden:

$this->UpdateFormField(‚TriggerList‘, ‚columns‘, json_encode($test));

$test beinhaltet jetzt nochmal alle columns oder kann ich gezielt eine Spalte auch updaten?
Im DynamicTest Modul (SymconTest) werden alle Spalten angegeben.

Uli

Einzelne Spalten können nicht aktualisiert werden, es ist immer die gesamte Ladung. Das ist also so korrekt.

Hi, ich würde das Thema gern noch mal aufmachen, ich habe mir schon einen Wolf gesucht, aber alles Versuche sind bisher gescheitert, nach einer Änderung nach einem onEdit Ereignis die Änderungen einer Zeile einer Liste zu aktualisieren.

Vielleicht sehe ich den Wald vor lauter Bäumen nicht…

Kann einer von Euch bitte mal einen kompletten Codeschnipsel dazu posten (also wie genau muss ich die Liste auslesen, ein Element in einer speziellen Zeile (die ja wohl beim onEdit Event irgendwie auszulesen sein muss) ändern und anschließend die Änderung in der Liste wieder anzeigen mit UpdateFormField…

Das wäre total toll!

LG Mirko

Ich habe das glaube ich damals verworfen und nicht genutzt, weil es für meinen Anwendungsfall nicht funktioniert hat.

Magst du hier mal schauen, ob etwas für dich dabei ist:

ListDynamicEditFormTest

Uli

Auszug aus der Doku:

onEdit (optional) (default: „“) Skript, welches nach der Bearbeitung eines Eintrages der Liste ausgeführt wird. Sofern das Skript aus mehreren Zeilen besteht, können die einzelnen Zeilen auch als Array übergeben werden (Arrays werden ab Version 6.0 unterstützt). In dieser Ausführung bietet die Liste als Variablenwert nicht den aktuell ausgewählten Eintrag sondern den bearbeiteten Eintrag, siehe Ausgewählte Zeile als Variable in on-Aktionen. Ansonsten hat es die gleichen Eigenschaften wie onClick des Button (ab IP-Symcon 5.3)

AUSGEWÄHLTE ZEILE ALS VARIABLE IN ON-AKTIONEN

//Gebe den Namen der aktuell gewählte Zeile der Tabelle devices aus
    { "type": "Button", "caption": "Ausgabe", "onClick": "print_r($devices['name']);" }

    //Gebe den Namen der ersten Zeile der Tabelle devices aus
    { "type": "Button", "caption": "Ausgabe", "onClick": "print_r($devices[0]['name']);" }

Uli

Habe das Thema eben gerade auch gehabt.
Beispiel kommt aber erst heute Abend. Erstmal Termine, Termine, Termine :grin:
Michael

Ich habe mal das Testmodul von Symcon als Basis genutzt:
Form:

        {
            "type": "List",
            "add": true,
            "name": "TestList",
            "rowCount": 7,
            "onEdit": "LOET_MyList($id,$TestList);",
            "columns": [{
                "name": "data",
                "caption": "Data",
                "add": 1,
                "width": "auto",
                "edit": {
                    "type": "NumberSpinner"
                }
            }]
        }

Modul:

        public function MyList($Liste)
        {
            // Aktueller Wert der gewählten / editierten Zeile:
            $NewValueData = $Liste['data']; // Geht mit allen Spalten
            $NewList = [];

            // Ganze Liste durchgehen, einfach $Liste benutzen geht nicht, da es ein spezielles Objekt ist.
            foreach ($Liste as &$Entry) {
                if ($Entry['data'] == $NewValueData) {
                    $Entry['data']++;
                }
                $NewList[] = $Entry;
            }
			// Neues Values Feld bauen und an Console senden
            $this->UpdateFormField('TestList', 'values', json_encode($NewList));
        }

Man muss hier beachten wie man auf die Liste zugreift.
Nummerische Indexes sind die Zeilen
Und String Indexes sind die Werte der Spalten der gerade editierten Zeile.
Michael