2008-08-18 16 views
13

Ich habe nur ein bisschen Flex-Entwicklung bis jetzt gemacht, aber ich habe den Ansatz der Erstellung von Steuerelementen programmgesteuert über Mxml-Dateien, weil (und bitte, korrigieren Sie mich, wenn ich falsch liege!) Ich habe gesammelt dass Sie nicht beide Möglichkeiten haben können - das heißt, dass Sie die Klassenfunktion in einer separaten ActionScript-Klassendatei haben, aber die enthaltenen Elemente in mxml deklariert haben.Flex: Gibt es eine schmerzfreie programmatische Datenbindung?

Es scheint nicht viel Unterschied produktiv zu sein, aber Datenbindung programmatisch scheint etwas weniger als trivial. Ich habe mir angesehen, wie der mxml-Compiler die Datenbindungsausdrücke transformiert. Das Ergebnis ist eine Reihe generierter Callbacks und viel mehr Zeilen als in der mxml-Repräsentation. Also hier ist die Frage: gibt es eine Möglichkeit, Datenbindung programmatisch zu tun, die keine Welt der Verletzung beinhaltet?

Antwort

29

Haben Sie keine Angst vor MXML. Es ist großartig zum Auslegen von Ansichten. Wenn Sie Ihre eigenen wiederverwendbaren Komponenten schreiben, kann Ihnen das Schreiben in ActionScript manchmal ein wenig mehr Kontrolle geben, aber für nicht wiederverwendbare Ansichten ist MXML viel besser. Es ist knapper, Bindungen sind extrem einfach einzurichten, etc.

Allerdings müssen Bindungen in reinem ActionScript nicht so viel Schmerz sein. Es wird nie so einfach wie in MXML sein, wo viele Dinge für Sie erledigt werden, aber es kann mit nicht zu viel Aufwand getan werden.

Was Sie haben, ist BindingUtils und es ist Methoden bindSetter und bindProperty. Ich benutze fast immer erstere, da ich normalerweise etwas arbeiten möchte, oder invalidateProperties aufrufen, wenn sich Werte ändern, ich möchte fast nie nur eine Eigenschaft setzen.

Was Sie wissen müssen ist, dass diese zwei ein Objekt des Typs ChangeWatcher zurückgeben, wenn Sie die Bindung aus irgendeinem Grund entfernen möchten, müssen Sie dieses Objekt festhalten. Dies macht manuelle Bindungen in ActionScript etwas weniger praktisch als die in MXML.

Beginnen wir mit einem einfachen Beispiel beginnen:

BindingUtils.bindSetter(nameChanged, selectedEmployee, "name"); 

Dies stellt eine Bindung, wird das Verfahren nameChanged wenn die name Eigenschaft auf dem Objekt in der Variablen selectedEmployee Änderungen nennen. Die nameChanged Methode wird den neuen Wert der name Eigenschaft als Argument empfängt, so sollte es so aussehen:

private function nameChanged(newName : String) : void 

Das Problem mit diesem einfachen Beispiel ist, dass wenn Sie diese Bindung eingerichtet haben es jedes Mal Feuer Die Eigenschaft des angegebenen Objekts ändert sich. Der Wert der Variablen selectedEmployee kann sich ändern, aber die Bindung ist immer noch für das Objekt eingerichtet, auf das die Variable zuvor gezeigt hat.

Es gibt zwei Möglichkeiten, dieses Problem zu lösen: entweder die ChangeWatcher zurück von BindingUtils.bindSetter um zu halten und unwatch auf mich anrufen, wenn Sie die Bindung (und dann die Einrichtung eine neue Bindung statt), oder binden, um sie entfernen mögen. Ich zeige dir zuerst die erste Option und erkläre dann, was ich meine, indem du dich an dich bindest.

Die currentEmployee könnte in einen Getter/Setter Paar und wie diese (nur die Setter zeigt) implementiert vorgenommen werden:

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 Eigenschaft festgelegt ist es, wenn es zu sehen, sieht ein vorheriger Wert, und wenn so die Bindung für das Objekt entfernt (currentEmployeeNameCW.unwatch()), dann wird die private Variable festgelegt, und der neue Wert null richtet eine neue Bindung für die Eigenschaft name ein. Am wichtigsten ist, dass es die ChangeWatcher speichert, die von dem Bindungsaufruf zurückgegeben wird.

Dies ist ein grundlegendes Bindungsmuster und ich denke, es funktioniert gut. Es gibt jedoch einen Trick, der verwendet werden kann, um es ein bisschen einfacher zu machen. Sie können stattdessen an sich binden. Anstatt Bindungen jedes Mal einzurichten und zu entfernen, wenn sich die Eigenschaft currentEmployee ändert, können Sie das Binding-System für Sie tun lassen.

BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]); 

Dies stellt eine Bindung nicht nur auf die currentEmployee Eigenschaft auf this, sondern auch auf die name Eigenschaft: In Ihrem creationComplete Handler (oder Konstruktor oder zumindest einige Zeit früh) können Sie eine Bindung wie so einrichten auf diesem Objekt. Also bei jeder Änderung wird entweder die Methode currentEmployeeNameChanged aufgerufen. Sie müssen die ChangeWatcher nicht speichern, da die Bindung nie entfernt werden muss.

Die zweite Lösung funktioniert in vielen Fällen, aber ich habe festgestellt, dass die ersten manchmal notwendig ist, vor allem, wenn sie mit Bindungen in nicht-Ansicht Klassen arbeiten (da this hat ein Event-Dispatcher sein und die currentEmployee muss sein bindefähig, damit es funktioniert).

+0

Groß schreiben. Das war sehr hilfreich. Vielen Dank! – airportyh

+0

Großartig in der Tat schreiben. – Kevin

+0

Awesome write-up in der Tat! Eine kurze Frage. Wäre ein einfacherer Ansatz für die erste Lösung nicht einfach zu überprüfen, ob currentEmployeeNameCW null ist? Wenn es nicht null ist, dann existiert eine Bindung. Rufen Sie currentEmployeeNameCW.unwatch() auf. Scheint wie eine verallgemeinerte und immer noch sehr prägnante Lösung. – rinogo

2

Eine Möglichkeit, MXML und ActionScript für eine Komponente in separate Dateien zu trennen, besteht darin, etwas Ähnliches wie beim ASP.Net 1.x-Code hinter dem Modell zu tun. In diesem Modell ist der deklarative Teil (in diesem Fall die MXML) eine Unterklasse des Imperativteils (ActionScript). So könnte ich den Code hinter für eine Klasse wie folgt erklären:

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 wie folgt aus:

<?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 aus diesem Beispiel sehen können, ein disadvatage dieses Ansatzes ist, dass Sie in beiden Dateien Steuerelemente wie myLabel deklarieren müssen.

+0

Der gute Grund dafür ist, dass es die Flex-Design-Ansicht nicht durchbricht. Wenn Sie sich für die Design-Ansicht nicht so sehr interessieren (wie ich), dann ist es viel einfacher, mit der Vererbung umzugehen. Sie müssen die mxml-untergeordneten Elemente in Ihrer .as-Klasse nicht vorab deklarieren (und synchronisieren). Es fühlt sich auch "richtiger" an. Nehmen Sie eine visuelle und fügen Sie Funktionalität hinzu. –

+0

Tun Sie dies nicht ... es verdoppelt die Anzahl der Klassen, die Sie für eine Ansicht benötigen. – Clintm

0

Es gibt eine Möglichkeit, die ich normalerweise verwende, um mxml und action script zusammen zu verwenden: Alle meine mxml-Komponenten erben von einer Action-Script-Klasse, wo ich den komplexeren Code hinzufüge. Dann können Sie auf Ereignislistener verweisen, die in dieser Klasse in der mxml-Datei implementiert sind.

Grüße,

Ruth

8

Es ab heute existiert. :)

Ich habe gerade mein Actionscript-Datenbindung Projekt als Open Source: http://code.google.com/p/bindage-tools

BindageTools ist eine Alternative zu BindingUtils (es das Wortspiel sehen?), Die ein fließend-API verwendet, wo Sie Ihre Datenbindungen in einem Pipeline-Stil erklären:

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

Etc Es gibt viele weitere Beispiele auf der Website. Es gibt noch viele andere Features - komm, sieh es dir an. --Matthew

Bearbeiten: aktualisierte APIs

+0

es ist so toll jemanden zu finden, der das gemacht hat, in reiner as3 weise danke viel ~ – davyzhang

+0

Mein Vergnügen, froh, dass Sie es nützlich finden. – qualidafial