Flex: существует безболезненная программная привязка данных?

До сих пор я только немного занимался разработкой Flex, но я предпочел подход к созданию элементов управления программно над файлами mxml, потому что (и, пожалуйста , поправьте меня, если я ошибаюсь!) Я понял, что вы можете ' у него оба пути, то есть функциональность класса в отдельном файле класса ActionScript, но содержащиеся в нем элементы объявлены в mxml.

По-видимому, с точки зрения производительности не существует особой разницы, но связывание данных программным способом кажется несколько менее тривиальным. Я взглянул на то, как компилятор mxml преобразует выражения привязки данных. В результате получается множество сгенерированных обратных вызовов и намного больше строк, чем в представлении mxml. Итак, вот вопрос: есть ли способ связывания данных программным способом, который не затрагивает мир вреда?

18.08.2008 06:15:08
4 ОТВЕТА
РЕШЕНИЕ

Не бойся MXML. Это отлично подходит для выкладки взглядов. Если вы пишете свои собственные повторно используемые компоненты, то написание их в ActionScript может иногда дать вам немного больше контроля, но для многоразовых представлений MXML намного лучше. Это более кратко, привязки очень легко установить и т. Д.

Однако привязки в чистом ActionScript не должны быть такой большой болью. Это никогда не будет так просто, как в MXML, где многое для вас сделано, но это можно сделать не слишком большими усилиями.

Что у вас есть BindingUtilsи это методы bindSetterи bindProperty. Я почти всегда использую первое, так как я обычно хочу выполнять какую-то работу или вызывать invalidatePropertiesпри изменении значений, я почти никогда не хочу просто устанавливать свойство.

Что вам нужно знать, так это то, что эти два возвращают объект типа ChangeWatcher, если вы хотите удалить привязку по какой-то причине, вы должны держаться за этот объект. Это то, что делает ручные привязки в ActionScript немного менее удобными, чем в MXML.

Давайте начнем с простого примера:

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

Это устанавливает привязку, которая будет вызывать метод nameChangedпри изменении nameсвойства объекта в переменной selectedEmployee. nameChangedМетод Получать новое значение nameимущества в качестве аргумента, поэтому он должен выглядеть следующим образом :

private function nameChanged( newName : String ) : void 

Проблема этого простого примера заключается в том, что после настройки этой привязки она будет срабатывать каждый раз, когда изменяется свойство указанного объекта. Значение переменной selectedEmployeeможет измениться, но привязка все еще устанавливается для объекта, на который переменная указала ранее.

Есть два способа решить эту проблему: либо сохранить ChangeWatcherвозвращаемое BindingUtils.bindSetterокружение и вызвать unwatchего, когда вы хотите удалить привязку (а затем вместо этого установить новую привязку), либо привязать к себе. Сначала я покажу вам первый вариант, а затем объясню, что я подразумеваю под привязкой к себе.

Они currentEmployeeмогут быть преобразованы в пару геттер / сеттер и реализованы следующим образом (только показывая сеттер):

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

Что происходит, так это то, что когда currentEmployeeсвойство установлено, оно проверяет, было ли предыдущее значение, и, если это так, удаляет привязку для этого объекта ( currentEmployeeNameCW.unwatch()), то устанавливает частную переменную, и если новое значение не nullустанавливает новую привязку. для nameсобственности. Самое главное, что он сохраняет ChangeWatcherвозвращенные вызовом привязки.

Это базовый шаблон привязки, и я думаю, что он работает нормально. Однако есть хитрость, которую можно использовать, чтобы сделать ее немного проще. Вы можете привязать к себе вместо этого. Вместо того, чтобы устанавливать и удалять привязки каждый раз, когда currentEmployeeизменяется свойство, вы можете сделать так, чтобы система привязок сделала это за вас. В вашем creationCompleteобработчике (или конструкторе или, по крайней мере, раньше) вы можете установить привязку следующим образом:

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

Это устанавливает привязку не только к currentEmployeeсвойству this, но и к nameсвойству этого объекта. Поэтому в любое время любой из этих методов currentEmployeeNameChangedбудет вызван. Нет необходимости сохранять, ChangeWatcherпотому что привязка никогда не будет удалена.

Второе решение работает во многих случаях, но я обнаружил, что первое иногда необходимо, особенно при работе с привязками в классах, не являющихся представлениями (поскольку thisон должен быть диспетчером событий и currentEmployeeдолжен быть привязываемым, чтобы он работал) ,

29
18.08.2008 06:56:22
Действительно потрясающая рецензия! Быстрый вопрос Разве более простой подход к первому решению просто состоит в том, чтобы проверить, является ли currentEmployeeNameCW нулевым? Если оно не равно нулю, то привязка существует, поэтому вызовите currentEmployeeNameCW.unwatch (). Похоже на более обобщенное и все еще очень сжатое решение.
rinogo 15.06.2010 23:10:57
Код делает несколько вещей: он гарантирует, что мы не будем обновлять, если значение не отличается, он обновляет переменную и наблюдатель, и позволяет чисто сбросить все, задав для свойства нулевое значение (он корректно обрабатывает оба случай, когда переменная равна нулю, и когда она установлена ​​в нуль, и наблюдает и наблюдает по мере необходимости). Я уверен, что это может быть написано по-другому, но это должно делать все эти вещи, и я не уверен, что вы могли бы написать это намного короче, чем это, без потери функциональности.
Theo 16.06.2010 14:28:50
Не будет ли второй метод продолжать связывать данные, даже если компонент больше не виден, но все еще находится в памяти?
adamk 28.01.2011 18:54:08

Один из способов разделить MXML и ActionScript для компонента на отдельные файлы - сделать что-то похожее на код ASP.Net 1.x, стоящий за моделью. В этой модели декларативная часть (в данном случае MXML) является подклассом императивной части (ActionScript). Так что я мог бы объявить код для такого класса:

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

... и разметка вот так:

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

Как видно из этого примера, недостаток этого подхода заключается в том, что вы должны объявить элементы управления, такие как myLabel, в обоих файлах.

2
30.08.2008 14:54:16
Хорошая причина сделать это таким образом, что это не нарушает представление о гибком дизайне. Если вы не очень заинтересованы в представлении проекта (как и я), тогда проще будет выполнить наследование другим способом. Вам не нужно предварительно объявлять (и поддерживать синхронизацию) дочерние элементы mxml в своем классе .as. Также он чувствует себя более "правильным". Возьмите визуальный элемент и добавьте к нему функциональность.
Marc Hughes 28.01.2011 20:19:41
Не делайте этого ... это удвоит количество классов, необходимых для просмотра.
Clintm 14.02.2017 19:04:06

есть способ, который я обычно использую для совместного использования mxml и сценария действия: все мои компоненты mxml наследуются от класса сценария действия, в который я добавляю более сложный код. Затем вы можете обратиться к обработчикам событий, реализованным в этом классе, в файле mxml.

С Уважением,

Рут

0
4.02.2009 14:49:59

Он существует на сегодняшний день. :)

Я только что выпустил свой проект связывания данных ActionScript с открытым исходным кодом: http://code.google.com/p/bindage-tools

BindageTools - это альтернатива BindingUtils (смотрите игру слов?), В которой используется свободный API, в котором вы объявляете привязки данных в стиле конвейера:

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

Двусторонние привязки:

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

Явное преобразование и проверка данных:

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

И т.д. На сайте есть еще много примеров. Там также много других функций - приходите посмотреть. --Мэтью

Изменить: обновленные API

8
7.06.2011 22:26:08