Так случилось, что фильтрация коллекций ни разу не беспокоила меня. А тут коллега задал вопрос, который послужил толчком к тому, что вы сейчас прочитаете. =)

Коллеге необходимо показать во view отфильтрованную ArrayCollection. Сама ArrayCollection находится в модели. Когда во view применяется фильтр, то данные модели очень сильно выглядят измененными. Что делать? Как быть?

Конечно, мы с вами знаем, что данные на самом деле не меняются. source коллекции так и остается исходным. Но не работать же в модели с source?

Конечно, не работать! И уж конечно, не делать копии коллекций! Тут спасибо Константинеру за наводку. =)

Можно использовать ListCollectionView в качестве обертки нашей исходной коллекции уже на уровне view. Тогда довольная модель работает с ArrayCollection, как и прежде, а view показывает, что захочет.

В качестве примера использую пример из доков Adobe для фильтрации коллекций.
Пример такой:

XML:

<?xml version="1.0"?>
<!−− dpcontrols\SortFilterArrayCollection.mxml −−>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="600">
    <mx:Script>
        <![CDATA[
            import mx.collections.*;
    
            /* Function to sort the ICollectionView
               in ascending order. */
            public function sortAC():void {
                var sortA:Sort = new Sort();
                sortA.fields=[new SortField("label")];
                myAC.sort=sortA;
                //Refresh the collection view to show the sort.
                myAC.refresh();
            }
 
            /* Function to filter out all items with labels
               that are not in the range of M-N. */
            public function stateFilterFunc(item:Object):Boolean {
                return item.label >= "M" && item.label < "O";
            }
            
            /* Function to apply the filter function the ICollectionView. */
            public function filterAC():void {
                myAC.filterFunction=stateFilterFunc;
                /* Refresh the collection view to apply the filter. */
                myAC.refresh();
            }
 
            /* Function to Reset the view to its original state. */
            public function resetAC():void {
                myAC.filterFunction=null;
                myAC.sort=null;
                //Refresh the collection view.
                myAC.refresh();
            }
 
        ]]>
    </mx:Script>
 
    <!−− An ArrayCollection with an array of objects. −−>
    <mx:ArrayCollection id="myAC">
        <mx:Array id="myArray">
            <mx:Object label="LA" data="Baton Rouge"/>
            <mx:Object label="NH" data="Concord"/>
            <mx:Object label="TX" data="Austin"/>
            <mx:Object label="MA" data="Boston"/>
            <mx:Object label="AZ" data="Phoenix"/>
            <mx:Object label="OR" data="Salem"/>
            <mx:Object label="FL" data="Tallahassee"/>
            <mx:Object label="MN" data="Saint Paul"/>
            <mx:Object label="NY" data="Albany"/>
        </mx:Array>
    </mx:ArrayCollection>
 
    <!-- Buttons to filter, sort, or reset the view in the second ComboBox
            control. -->
    <mx:HBox width="100%">
        <mx:Button id="sortButton" label="Sort" click="sortAC();"/>
        <mx:Button id="filterButton" label="Filter" click="filterAC();"/>
        <mx:Button id="resetButton" label="Reset" click="resetAC();"/>
    </mx:HBox>
    <mx:VBox width="550" height="143" borderStyle="solid" paddingTop="10" paddingLeft="10">
        <mx:Label text="This box retains original order and contents of the Array:"/>
        <!-- A ComboBox populated by the underlying Array object.
            This control shows that Array retains its original order. -->
        <mx:ComboBox id="cb2" rowCount="10" dataProvider="{myArray}"/>
        <mx:HRule/>
        <mx:Label text="This box reflects the changes to the Array:"/>
        <!−− A ComboBox populated by the collection view of the Array. −−>
        <mx:ComboBox id="cb1" rowCount="10" dataProvider="{myAC}"/>
    </mx:VBox>
</mx:Application>

Тут у нас два выпадающих списка: один с source коллекции, а второй с самой коллекцией.
Можно отсортировать, отфильтровать коллекцию и посмотреть, что будет.
После выполнения метода filterAC наша коллекция уже сама не своя, ее прямо не узнать.

Что же мы делаем? Тут и далее использован беглый стиль программирования, потому что все-таки пример. =)
Добавляем обертку для коллекции:

Code:

[Bindable]
private var wrapper:ListCollectionView;

После создания приложения создаем эту нашу обертку:

Code:

creationComplete="wrapper=new ListCollectionView(myAC)"

Фильтруем уже эту обертку, ее же показываем во втором выпадающем списке.
Добавляем новый выпадающий список для нашей родной коллекции.

Вот как выглядит код после изменений:

XML:

<?xml version="1.0"?>
<!−− dpcontrols\SortFilterArrayCollection.mxml −−>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="600"
  creationComplete="wrapper=new ListCollectionView(myAC)">
    <mx:Script>
        <![CDATA[
 
            import mx.collections.*;
            
            [Bindable]
            private var wrapper:ListCollectionView;
    
            /* Function to sort the ICollectionView
               in ascending order. */
            public function sortAC():void {
                var sortA:Sort = new Sort();
                sortA.fields=[new SortField("label")];
                wrapper.sort=sortA;
                //Refresh the collection view to show the sort.
                wrapper.refresh();
            }
 
            /* Function to filter out all items with labels
               that are not in the range of M-N. */
            public function stateFilterFunc(item:Object):Boolean {
                return item.label >= "M" && item.label < "O";
            }
            
            /* Function to apply the filter function the ICollectionView. */
            public function filterAC():void {
                wrapper.filterFunction=stateFilterFunc;
                /* Refresh the collection view to apply the filter. */
                wrapper.refresh();
            }
 
            /* Function to Reset the view to its original state. */
            public function resetAC():void {
                wrapper.filterFunction=null;
                wrapper.sort=null;
                //Refresh the collection view.
                wrapper.refresh();
            }
 
        ]]>
    </mx:Script>
 
    <!−− An ArrayCollection with an array of objects. −−>
    <mx:ArrayCollection id="myAC">
        <mx:Array id="myArray">
            <mx:Object label="LA" data="Baton Rouge"/>
            <mx:Object label="NH" data="Concord"/>
            <mx:Object label="TX" data="Austin"/>
            <mx:Object label="MA" data="Boston"/>
            <mx:Object label="AZ" data="Phoenix"/>
            <mx:Object label="OR" data="Salem"/>
            <mx:Object label="FL" data="Tallahassee"/>
            <mx:Object label="MN" data="Saint Paul"/>
            <mx:Object label="NY" data="Albany"/>
        </mx:Array>
    </mx:ArrayCollection>
 
    <!-- Buttons to filter, sort, or reset the view in the second ComboBox
            control. -->
    <mx:HBox width="100%">
        <mx:Button id="sortButton" label="Sort" click="sortAC();"/>
        <mx:Button id="filterButton" label="Filter" click="filterAC();"/>
        <mx:Button id="resetButton" label="Reset" click="resetAC();"/>
    </mx:HBox>
    <mx:VBox width="550" borderStyle="solid" paddingTop="10" paddingLeft="10">
        <mx:Label text="This box retains original order and contents of the Array:"/>
        <!-- A ComboBox populated by the underlying Array object.
            This control shows that Array retains its original order. -->
        <mx:ComboBox id="cb2" rowCount="10" dataProvider="{myArray}"/>
        <mx:HRule/>
        <mx:Label text="This box reflects the changes to the Wrapper:"/>
        <!−− A ComboBox populated by the collection view of the Array. −−>
        <mx:ComboBox id="cb1" rowCount="10" dataProvider="{wrapper}"/>
        <mx:HRule/>
        <mx:Label text="This box reflects the changes to the ArrayCollection:"/>
        <!−− A ComboBox populated by the collection view of the Array. −−>
        <mx:ComboBox rowCount="10" dataProvider="{myAC}"/>
    </mx:VBox>
</mx:Application>

В результате получаем фильтрацию исключительно на уровне view. А модель живет своей счастливой и долгой жизнью. =)

Этот же подход можно использовать и при необходимости показывать одну и ту же коллекцию в разных контролах с разными фильтрами.

Трекбек адрес этой записи

URL трекбека (щелкните правой кнопкой мыши и скопируйте ссылку)

2 комментариев

Комментарий от: graFF [Посетитель] · http://graffixtation.blogspot.com
в свое время тоже из-за этого столько дров наломал
пошел переписывать приложение, а то там все на костылях)))
2010-03-10 @ 15:18
пошел переписывать приложение, а то там все на костылях)))

Лучше поздно, чем никогда. =)
2010-03-10 @ 16:02

Оставить комментарий


Ваш email адрес. (Не будет показан на сайте.)

Ваш URL будет показан.
:!: :?: :idea: :) :D :p B) ;) :> :roll: :oops: :| :-/ :( :'( |-| :>> :yes: ;D :P :)) 88| :. :no: XX( :lalala: :crazy: >:XX
(Заменить прерывания строк на <br />)
(Имя, email и сайт)
(Разрешить пользователям посылать вам сообщения (ваш email не отображается).)
3 + 2 + 7 - 1?
antispam test

Вы можете использовать OpenID чтобы предоставить ваше имя, email и url.