major
#29810
TL Views: the selection channel of a <tree> keeps an object deleted in another view, and scripts over it fail
tl-layout-view 8.0.0-alpha10, TreeElement and the channels reading its selection.
Problem
A <tree> whose selected node is a persistent object keeps that object in its selection channel after the object was deleted in another view (here: a Connection node of the group address tree, removed through a command of the device view in another tab, or by a command of the same view that deletes the selected node and selects its parent only afterwards). Every derived channel or <visible-if> over the selection then evaluates its expression on the deleted object and fails with
Auswertung von '($n instanceof `ehome.physical:Connection`)' fehlgeschlagen … (DeletedObjectAccess)
shown as an error toast; the view stays in that state until another node is selected. A <table> drops a selection whose key is no longer among its rows (#29535); the tree does not.
Reproduced in the React demo (Baum-Demo): select a ticket, delete it from a second browser tab, then navigate away and back to the tree demo. The navigation fails with the error toast, and a reload of the window answers HTTP 500 - the window cannot show the view any more. The failing evaluation is the model-access executability rule of the toolbar commands, which asks the deleted object for its type when the commands re-attach, before the tree could catch up. The same happens with any channel nobody re-writes, e.g. the activated channel of the tree demo after the activated milestone was deleted.
Expected
When a selected node's object is deleted (the tree observes its observed-types anyway), the tree drops it from the selection (or selects the parent), and the selection channel never holds an invalid object. At least instanceOf and attribute access in TL-Script should treat a deleted object as null instead of throwing.
Workaround in E-Home
A TL-Script function existingObject(x) (Java) that returns null for a deleted object, wrapped around every reader of the tree selection in projects/addresses.view.xml; the remove command selects the parent address before deleting.
Lösung
- Ein Kanal lässt ein gelöschtes Objekt selbst fallen und meldet das als gewöhnliche Änderung (DefaultViewChannel). Solange die Anzeige, die den Kanal nutzt, auf dem Bildschirm ist, beobachtet der Kanal die Objekte seines Werts; wird eines gelöscht, setzt er den Wert ohne dieses Objekt (Einzelobjekt → null, Menge → der Rest) über die normale Änderungsmeldung, ohne die Veto-Prüfung (auf einem gelöschten Objekt gibt es nichts zu speichern). Beim Wiederanzeigen prüft er seinen Wert ebenso. Jeder Leser - Text, abgeleiteter Kanal, <visible-if>, Kommando-Ausführbarkeit, Dialog - aktualisiert sich über seinen vorhandenen Kanal-Listener, im selben Request wie das Löschen.
- Der Selektor behält die Wahl der Folgeauswahl. Die Bindungen von Baum und Tabelle erkennen einen Kanalwert, der nur gelöschte angezeigte Objekte weglässt, als keine neue Auswahl: Sie behalten ihre Erinnerung an die angezeigte Auswahl, und ihre eigene Strukturänderung bzw. Aktualisierung wählt und schreibt die Folgeauswahl - der Baum den Knoten, in dem der verschwundene Knoten hing (den nächsten noch angezeigten Vorgänger; die Wurzel nur, wenn sie angezeigt wird), die Tabelle die überlebende Auswahl. Eine Mehrfachauswahl mit überlebenden Knoten behält diese.
- Keine Lücke bei implizit erzeugten Kanälen. ObservingChannels.observeWhileDisplayed(root, ownedChannels, context) bindet die Beobachtung eines Kanals an die Anzeige des Teilbaums, dem er dient; genutzt von der View für ihre deklarierten Kanäle sowie von Dialogen (Parameter), Kachelrahmen, Template-Instanzen, Wizard-Schritten, der Objektliste (Neuanlage-Entwurf), Menüs, dem WYSIWYG-Editor und dem Designer für die Kanäle, die sie erzeugen. Nur der Erzeuger bindet einen Kanal; ein per <bind> hereingereichter Kanal bleibt bei der äußeren View.
- Leser behandeln ein gelöschtes Objekt als fehlenden Wert, wo eines sie dennoch erreicht (Kanäle ohne Anzeige, Skripte über fremde Objekte): TL-Script instanceOf liefert false, Attributzugriff (get, $x.attr, $x["name"]) den leeren Wert, referers eine leere Menge; LiveExecutability zählt eine gelöschte Eingabe als fehlende Eingabe. Ein Objektbeobachter (ChannelObjectObserver) überspringt ein Ereignis, solange sein Kanal noch ein gelöschtes Objekt hält, das der Kanal in derselben Zustellung fallen lässt.
- Die Artikel view-layer/channels (Abschnitt "Deleted objects on a channel") und view-layer/tables beschreiben das Verhalten; der Kommentar der Baum-Demo ist angepasst.
Der E-Home-Workaround (existingObject(x), Elternauswahl vor dem Löschen) wird damit überflüssig.