Flex: czy istnieje bezbolesne programowe powiązanie danych?

Do tej pory zrobiłem tylko trochę Flex development, ale wolałem podejście tworzenia kontrolek programowo niż pliki mxml, ponieważ (i Proszę, popraw mnie, jeśli się mylę!) Doszedłem do wniosku, że nie można mieć tego w obie strony-to znaczy mieć funkcjonalność klasy w osobnym pliku klasy ActionScript, ale mieć zawarte elementy zadeklarowane w mxml.

Wydaje się, że nie ma dużej różnicy pod względem wydajności, ale łączenie danych programowo wydaje się nieco mniej niż trywialne. Przyjrzałem się jak kompilator MXML przekształca wyrażenia wiążące dane. Rezultatem jest kilka wygenerowanych wywołań zwrotnych i dużo więcej linii niż w reprezentacji mxml. Więc oto pytanie: czy istnieje sposób na programowanie wiązania danych, który nie wiąże się ze światem bólu?

Author: Theo, 2008-08-18

4 answers

Nie bój się MXML. Świetnie nadaje się do układania widoków. Jeśli napiszesz własne komponenty wielokrotnego użytku , zapisanie ich w ActionScript może czasami dać ci nieco większą kontrolę, ale w przypadku widoków wielokrotnego użytku MXML jest znacznie lepszy. Jest bardziej zwięzły, wiązania są niezwykle łatwe w konfiguracji itp.

Jednak wiązania w czystym Actionscripcie nie muszą być aż tak bolesne. To nigdy nie będzie tak proste jak w MXML, gdzie wiele rzeczy jest robionych za ciebie, ale można to zrobić nie za dużo wysiłku.

To co masz to BindingUtils i to metody bindSetter i bindProperty. Prawie zawsze używam tego pierwszego, ponieważ zazwyczaj chcę wykonać jakąś pracę lub wywołać invalidateProperties gdy wartości się zmieniają, prawie nigdy nie chcę po prostu ustawić właściwości.

Musisz wiedzieć, że te dwa obiekty zwracają obiekt typu ChangeWatcher, jeśli chcesz usunąć powiązanie z jakiegoś powodu, musisz trzymać się tego obiektu. To sprawia, że ręczne wiązania w języku ActionScript są nieco mniej wygodne niż te w MXML.

Zacznijmy od prostego przykładu:

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

Ustawia powiązanie, które wywoła metodę nameChanged, gdy zmieni się właściwość name obiektu w zmiennej selectedEmployee. Metoda nameChanged otrzyma nową wartość właściwości name jako argument, więc powinna wyglądać tak:

private function nameChanged( newName : String ) : void 

Problem w tym prostym przykładzie polega na tym, że po skonfigurowaniu tego powiązania będzie ono uruchamiane za każdym razem, gdy zmieni się właściwość określonego obiektu. Wartość zmienna selectedEmployee może się zmienić, ale powiązanie jest nadal ustawione dla obiektu, na który wskazywała wcześniej zmienna.

Istnieją dwa sposoby rozwiązania tego problemu: albo zachować ChangeWatcher zwrócone przez BindingUtils.bindSetter i wywołać unwatch na nim, gdy chcesz usunąć Wiązanie (a następnie utworzyć nowe Wiązanie), albo powiązać z sobą. Najpierw pokażę Ci pierwszą opcję, a potem wyjaśnię, co mam na myśli, wiążąc się z Tobą.

currentEmployee można przekształcić w parę getter/setter i zaimplementowane w ten sposób (tylko pokazując 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");
        }
    }
}

Dzieje się tak, że gdy właściwość currentEmployee jest ustawiona, sprawdza, czy była poprzednia wartość, a jeśli tak, usuwa powiązanie dla tego obiektu (currentEmployeeNameCW.unwatch()), wtedy ustawia prywatną zmienną i chyba że nowa wartość to null ustawia Nowe powiązanie dla właściwości name. Co najważniejsze, zapisuje ChangeWatcher zwrócone przez wywołanie wiążące.

Jest to podstawowy wzór wiązania i myślę, że działa dobrze. Jest, jednak sztuczka, która może być użyta, aby uczynić ją nieco prostszą. Możesz związać się z samym sobą. Zamiast konfigurować i usuwać wiązania za każdym razem, gdy zmienia się właściwość currentEmployee, możesz poprosić system wiązań, aby zrobił to za Ciebie. W Twoim creationComplete handlerze (lub konstruktorze lub przynajmniej jakiś czas wcześniej) możesz skonfigurować Wiązanie w taki sposób:

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

To ustawia powiązanie nie tylko z właściwością currentEmployee na this, ale także z właściwością name na tym obiekcie. Więc w każdej chwili albo zmienia metodę currentEmployeeNameChanged zostanie wywołana. Nie ma potrzeby zapisywania ChangeWatcher, ponieważ Wiązanie nigdy nie będzie musiało zostać usunięte.

Drugie rozwiązanie działa w wielu przypadkach, ale odkryłem, że pierwsze jest czasami konieczne, szczególnie podczas pracy z wiązaniami w klasach nie-view (ponieważ this musi być dyspozytorem zdarzeń, a currentEmployee musi być bindowalne, aby działało).

 29
Author: Theo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-18 06:56:22

Istnieje do dziś. :)

Właśnie opublikowałem mój projekt powiązania danych ActionScript jako open source: http://code.google.com/p/bindage-tools

BindageTools jest alternatywą dla BindingUtils(zobacz grę słów tam?), który używa płynnego API, w którym deklaruje się powiązania danych w stylu potokowym:

Bind.fromProperty(person, "firstName")
    .toProperty(firstNameInput, "text");

Wiązania dwukierunkowe:

Bind.twoWay(
    Bind.fromProperty(person, "firstName"),
    Bind.fromProperty(firstNameInput, "text"));

Konwersja i walidacja danych jawnych:

Bind.twoWay(
    Bind.fromProperty(person, "age")
        .convert(valueToString()),
    Bind.fromProperty(ageInput, "text")
        .validate(isNumeric()) // (Hamcrest-as3 matcher)
        .convert(toNumber()));

Itd. Na stronie jest dużo więcej przykładów. Jest też wiele innych funkcji-zobacz. -- Mateusz

Edit: zaktualizowane interfejsy API

 8
Author: qualidafial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-07 22:26:08

Jednym ze sposobów oddzielenia MXML i ActionScript dla komponentu w osobne pliki jest zrobienie czegoś podobnego do ASP.Net 1.kod x za modelem. W tym modelu część deklaratywna (w tym przypadku MXML) jest podklasą części imperatywnej (ActionScript). Więc mogę zadeklarować kod za klasą taką jak Ta:

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.";
        }
    }
}

...a znacznik taki:

<?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>

Jak widać na tym przykładzie, wadą tego podejścia jest to, że musisz zadeklarować kontrole jak myLabel w obu plikach.

 2
Author: Nick Higgs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-30 14:54:16

Jest sposób, w jaki zwykle używam mxml i action script razem: wszystkie moje komponenty MXML dziedziczą z klasy action script, w której dodaję bardziej złożony kod. Następnie możesz odwołać się do detektorów zdarzeń zaimplementowanych w tej klasie w pliku mxml.

Pozdrawiam,

Ruth

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-04 14:49:59