Biegen:Gibt es eine schmerzlose programmatische Datenbindung?
-
08-06-2019 - |
Frage
Ich habe bisher nur ein wenig an der Flex-Entwicklung gearbeitet, aber ich habe den Ansatz, Steuerelemente programmgesteuert zu erstellen, gegenüber MXML-Dateien vorgezogen, weil (und Bitte, korrigieren Sie mich, wenn ich falsch liege!) Ich habe festgestellt, dass Sie nicht beide Möglichkeiten haben können – das heißt, die Klassenfunktionalität in einer separaten ActionScript-Klassendatei haben, aber die enthaltenen Elemente in mxml deklarieren lassen.
Hinsichtlich der Produktivität scheint es keinen großen Unterschied zu geben, aber die programmgesteuerte Datenbindung scheint alles andere als trivial zu sein.Ich habe mir angeschaut, wie der MXML-Compiler die Datenbindungsausdrücke transformiert.Das Ergebnis ist eine Menge generierter Rückrufe und viel mehr Zeilen als in der MXML-Darstellung.Hier ist also die Frage: Gibt es eine Möglichkeit, die Datenbindung programmgesteuert durchzuführen, ohne dass dabei jede Menge Ärger entsteht?
Lösung
Haben Sie keine Angst vor MXML.Es eignet sich hervorragend zum Anordnen von Ansichten.Wenn Sie Ihre eigenen schreiben wiederverwendbar Komponenten zu erstellen und sie dann in ActionScript zu schreiben, gibt Ihnen manchmal etwas mehr Kontrolle, aber für nicht wiederverwendbare Ansichten ist MXML viel besser.Es ist prägnanter, Bindungen lassen sich extrem einfach einrichten usw.
Bindungen in reinem ActionScript müssen jedoch nicht so mühsam sein.Es wird nie so einfach sein wie in MXML, wo viele Dinge für Sie erledigt werden, aber es kann mit nicht allzu großem Aufwand erledigt werden.
Was Sie haben, ist BindingUtils
und es sind Methoden bindSetter
Und bindProperty
.Ich verwende fast immer Ersteres, da ich normalerweise etwas arbeiten oder anrufen möchte invalidateProperties
Wenn sich Werte ändern, möchte ich fast nie einfach nur eine Eigenschaft festlegen.
Sie müssen wissen, dass diese beiden ein Objekt dieses Typs zurückgeben ChangeWatcher
, Wenn Sie aus irgendeinem Grund die Bindung entfernen möchten, müssen Sie dieses Objekt festhalten.Aus diesem Grund sind manuelle Bindungen in ActionScript etwas weniger praktisch als in MXML.
Beginnen wir mit einem einfachen Beispiel:
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
Dadurch wird eine Bindung eingerichtet, die die Methode aufruft nameChanged
wenn das name
Eigenschaft für das Objekt in der Variablen selectedEmployee
Änderungen.Der nameChanged
Die Methode erhält den neuen Wert von name
Eigenschaft als Argument, daher sollte es so aussehen:
private function nameChanged( newName : String ) : void
Das Problem bei diesem einfachen Beispiel besteht darin, dass diese Bindung nach dem Einrichten jedes Mal ausgelöst wird, wenn sich die Eigenschaft des angegebenen Objekts ändert.Der Wert der Variablen selectedEmployee
kann sich ändern, aber die Bindung ist weiterhin für das Objekt eingerichtet, auf das die Variable zuvor gezeigt hat.
Es gibt zwei Möglichkeiten, dieses Problem zu lösen:entweder um das zu behalten ChangeWatcher
zurückgegeben von BindingUtils.bindSetter
herumlaufen und anrufen unwatch
darauf, wenn Sie die Bindung entfernen (und dann stattdessen eine neue Bindung einrichten) oder sich selbst binden möchten.Ich zeige Ihnen zunächst die erste Option und erkläre dann, was ich unter „an sich selbst binden“ verstehe.
Der currentEmployee
könnte in ein Getter/Setter-Paar umgewandelt und wie folgt implementiert werden (zeigt nur den Setter):
public function set currentEmployee( employee : Employee ) : void {
if ( _currentEmployee != employee ) {
if ( _currentEmployee != null ) {
currentEmployeeNameCW.unwatch();
}
_currentEmployee = employee;
if ( _currentEmployee != null ) {
currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee, "name");
}
}
}
Was passiert ist, dass, wenn die currentEmployee
Wenn die Eigenschaft festgelegt ist, prüft sie, ob es einen vorherigen Wert gab, und entfernt in diesem Fall die Bindung für dieses Objekt (currentEmployeeNameCW.unwatch()
), dann legt es die private Variable fest, es sei denn, der neue Wert war null
richtet eine neue Bindung für die ein name
Eigentum.Am wichtigsten ist, dass es das spart ChangeWatcher
durch den Bindungsaufruf zurückgegeben.
Dies ist ein einfaches Bindungsmuster und ich denke, es funktioniert gut.Es gibt jedoch einen Trick, mit dem man es etwas einfacher machen kann.Stattdessen können Sie sich an sich selbst binden.Anstatt jedes Mal Bindungen einzurichten und zu entfernen currentEmployee
Wenn Sie Eigenschaftsänderungen vornehmen, können Sie diese vom Bindungssystem für Sie erledigen lassen.In deinem creationComplete
Handler (oder Konstruktor oder zumindest etwas früher) können Sie eine Bindung wie folgt einrichten:
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
Dadurch entsteht eine Bindung nicht nur an die currentEmployee
Eigentum auf this
, sondern auch zum name
Eigentum an diesem Objekt.Also jederzeit entweder die Methode ändern currentEmployeeNameChanged
wird angerufen werden.Es besteht keine Notwendigkeit, das zu speichern ChangeWatcher
denn die Bindung muss nie entfernt werden.
Die zweite Lösung funktioniert in vielen Fällen, aber ich habe festgestellt, dass die erste manchmal notwendig ist, insbesondere wenn mit Bindungen in Nicht-Ansichtsklassen gearbeitet wird (da this
muss ein Event-Dispatcher sein und der currentEmployee
muss bindbar sein, damit es funktioniert).
Andere Tipps
Es existiert seit heute.:) :)
Ich habe gerade mein ActionScript-Datenbindungsprojekt als Open Source veröffentlicht: http://code.google.com/p/bindage-tools
BindageTools ist eine Alternative zu BindingUtils (siehe das Wortspiel dort?), die eine fließende API verwendet, bei der Sie Ihre Datenbindungen im Pipeline-Stil deklarieren:
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Zwei-Wege-Bindungen:
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Explizite Datenkonvertierung und -validierung:
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Usw.Es gibt viele weitere Beispiele auf der Website.Es gibt auch viele andere Funktionen – schauen Sie sich um.--Matthew
Bearbeiten:aktualisierte APIs
Eine Möglichkeit, MXML und ActionScript für eine Komponente in separate Dateien aufzuteilen, besteht darin, etwas Ähnliches wie beim ASP.Net 1.x-Code-Behind-Modell zu tun.In diesem Modell ist der deklarative Teil (in diesem Fall MXML) eine Unterklasse des imperativen Teils (ActionScript).Ich könnte also den Code dahinter für eine Klasse wie diese deklarieren:
package CustomComponents
{
import mx.containers.*;
import mx.controls.*;
import flash.events.Event;
public class MyCanvasCode extends Canvas
{
public var myLabel : Label;
protected function onInitialize(event : Event):void
{
MyLabel.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
}
}
}
...und das Markup so:
<?xml version="1.0" encoding="utf-8"?>
<MyCanvasCode xmlns="CustomComponents.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="onInitialize(event)">
<mx:Label id="myLabel"/>
</MyCanvasCode>
Wie Sie diesem Beispiel entnehmen können, besteht ein Nachteil dieses Ansatzes darin, dass Sie Steuerelemente wie deklarieren müssen myLabel in beiden Dateien.
Es gibt eine Möglichkeit, die ich normalerweise verwende, um MXML und Aktionsskript zusammen zu verwenden:Alle meine MXML-Komponenten erben von einer Aktionsskriptklasse, der ich den komplexeren Code hinzufüge.Anschließend können Sie in der MXML-Datei auf die in dieser Klasse implementierten Ereignis-Listener verweisen.
Grüße,
Ruth