Кнопка удаления в ItemRenderer дает ошибку

Я использую следующий файл.as в качестве компонента для itemrenderer в списке. В основном каждый элемент отображается в TextInput, и каждый TextInput имеет кнопку удаления, как вы можете видеть из кода. При нажатии на кнопку удаления я хочу удалить selectedItem.. поэтому я помещаю функцию removeItem() в MainMxml.xml и вызываю ее из файла.as.

Однако я получаю сообщение об ошибке "Не удается получить доступ к методу или свойству нулевой ссылки на объект". Можете ли вы помочь мне с этой ошибкой?

Файл.as выглядит следующим образом:

package components {
    import flash.events.Event;
    import flash.events.MouseEvent;

    import mx.events.FlexEvent;

    import renderers.TextInputRenderer;

    import spark.components.Button;
    import spark.components.TextInput;
    import spark.events.TextOperationEvent;

    public class ClearableTextInput extends TextInput {

        [SkinPart(required="true")]
        public var clearButton:Button;

        [Bindable]
        public var mainMxml:MainMxml;


        public function ClearableTextInput() {
            super();

            //watch for programmatic changes to text property
            this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);

            //watch for user changes (aka typing) to text property
            this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
        }

        private function textChangedHandler(e:Event):void {
            if (clearButton) {
                clearButton.visible = (text.length > 0);
            }
        }

        private function clearClick(e:MouseEvent):void {
            mainMxml.removeItem();


        }


        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);

            if (instance == clearButton) {
                clearButton.addEventListener(MouseEvent.CLICK, clearClick);
                clearButton.visible = (text != null && text.length > 0);
            }           
        }

        override protected function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);

            if (instance == clearButton) {
                clearButton.removeEventListener(MouseEvent.CLICK, clearClick);
            }

        }
    }
}

И ItemRenderer как таковой:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
                xmlns:s="library://ns.adobe.com/flex/spark" 
                xmlns:mx="library://ns.adobe.com/flex/mx" 
                autoDrawBackground="true" xmlns:components="components.*" width="100%">

    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 

    <fx:Script>
        <![CDATA[
            import mx.core.EdgeMetrics;
            import mx.core.UIComponent;

            import skins.ClearableTextInputSkin;

        ]]>
    </fx:Script>


    <components:ClearableTextInput id="clearTxt" text="{data.label}" skinClass="skins.ClearableTextInputSkin" />

</s:ItemRenderer>

Я также устанавливаю кнопку очистки в ClearableTextInputSkin, которая показана ниже:

<?xml version="1.0" encoding="utf-8"?>
<s:SparkSkin
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        minWidth="100" minHeight="22"
        alpha.disabled="0.5"
        blendMode="normal">

    <fx:Metadata>
        [HostComponent("components.ClearableTextInput")]
    </fx:Metadata> 

    <!-- states -->
    <s:states>
        <s:State name="normal"/>
        <s:State name="disabled"/>
    </s:states>

    <!-- bg --> 
    <s:Rect id="border" left="0" right="0" top="0" bottom="0" radiusX="3">
        <s:fill>
            <s:SolidColor id="bgFill" color="#ffffff" />
        </s:fill>
        <s:stroke>
            <s:SolidColorStroke id="borderStroke" color="#333333" weight="1" />
        </s:stroke>
    </s:Rect>

    <!-- text -->
    <s:RichEditableText id="textDisplay" left="4" right="24" top="1" bottom="0"
            color="#333333"
            verticalAlign="middle" />



    <s:Button id="clearButton" right="4" verticalCenter="0" />

</s:SparkSkin>

Ваша помощь будет высоко ценится.

Большое спасибо.

1 ответ

Решение

Ответ может быть очень очень длинным:)

Начиная с вашего кода проблема заключается в следующем:

mainMxml.removeItem();

Ваш mainMxml пример null и именно поэтому у вас есть NPE (исключение нулевого указателя).

Но в целом код показывает, что вы в настоящее время не понимаете Flex, особенно привязку данных. И, конечно, есть проблемы с архитектурой приложения.

Во-первых, ваша строка:

[Bindable]
public var mainMxml:MainMxml;

ничего не делает

Привязка данных - это просто способ прослушать изменения переменной, помеченные [Bindable] метатегов. [Bindable] это не инъекция зависимости, а полная противоположность этому.

Так что никто не устанавливает ценность вашего mainMxml поле. И, по правде говоря, это не правильный способ попытаться ввести туда ценность. Вместо этого вы должны использовать шаблон проектирования Observer и событие fire из вашего компонента:

package events {
    public class ClearableTextInputEvent extends Event {
        public static const PERFORM_CLEAR:String = "performClear";
        public function ClearableTextInputEvent(type:String) {
            super(type);
        }

    }
}

Итак, теперь компонент:

package components {
    import flash.events.Event;
    import flash.events.MouseEvent;

    import mx.events.FlexEvent;

    import renderers.TextInputRenderer;

    import spark.components.Button;
    import spark.components.TextInput;
    import spark.events.TextOperationEvent;

    [Event(name="performClear", type="events.ClearableTextInputEvent")]
    public class ClearableTextInput extends TextInput {

        [SkinPart(required="true")]
        public var clearButton:Button;

        public function ClearableTextInput() {
            super();

            //watch for programmatic changes to text property
            this.addEventListener(FlexEvent.VALUE_COMMIT, textChangedHandler, false, 0, true);

            //watch for user changes (aka typing) to text property
            this.addEventListener(TextOperationEvent.CHANGE, textChangedHandler, false, 0, true);
        }

        private function textChangedHandler(e:Event):void {
            if (clearButton) {
                clearButton.visible = (text.length > 0);
            }
        }

        private function clearClick(e:MouseEvent):void {
            dispatchEvent(new ClearableTextInputEvent(ClearableTextInputEvent.PERFORM_CLEAR));
        }


        override protected function partAdded(partName:String, instance:Object):void {
            super.partAdded(partName, instance);

            if (instance == clearButton) {
                clearButton.addEventListener(MouseEvent.CLICK, clearClick);
                clearButton.visible = (text != null && text.length > 0);
            }           
        }

        override protected function partRemoved(partName:String, instance:Object):void {
            super.partRemoved(partName, instance);

            if (instance == clearButton) {
                clearButton.removeEventListener(MouseEvent.CLICK, clearClick);
            }

        }
    }
}

Хорошо. Теперь наш рендер. Там мы должны использовать всплывающее событие, чтобы сообщить нашему контейнеру списка (я полагаю, это экземпляр MainMxml) о необходимости убрать строку Мы должны создать класс событий для этого.

NB. Вы можете использовать тот же класс событий, но проблема в том, ClearableTextInput Компонент и элемент рендерера имеют разные обязанности и ClearableTextInput может быть снова использован в некоторых других рендерерах. Хорошей практикой является создание разных событий для разных уровней вашего приложения для слабой связи:

package events {
    public class RemoveRowEvent extends Event {
        public static const REMOVE_CURRENT_ROW:String = "removeCurrentRow";
        public function RemoveRowEvent(type:String) {
            super(type, true);
        }

    }
}

Теперь ваш рендер:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
                xmlns:s="library://ns.adobe.com/flex/spark" 
                xmlns:mx="library://ns.adobe.com/flex/mx" 
                autoDrawBackground="true" xmlns:components="components.*" width="100%">

    <s:layout> 
        <s:HorizontalLayout/> 
    </s:layout> 

    <fx:Script>
        <![CDATA[
            import mx.core.EdgeMetrics;
            import mx.core.UIComponent;

            import skins.ClearableTextInputSkin;

        ]]>
    </fx:Script>


    <components:ClearableTextInput id="clearTxt" text="{data.label}" skinClass="skins.ClearableTextInputSkin" performClear="dispatchEvent(new RemoveRowEvent(RemoveRowEvent.REMOVE_CURRENT_ROW))" />

</s:ItemRenderer>

И, наконец, в контейнере вашего списка (MainMxml экземпляр я полагаю)

…
addEventListener(RemoveRowEvent.REMOVE_CURRENT_ROW, onRowRemove);
…

private function onRowRemove(event:RemoveRowEvent):void {
    removeItem();
    event.stopImmediatePropagation();
}

Я написал этот черновик в браузере, поэтому, пожалуйста, исправьте импорт и т. Д. Самостоятельно:)

Другие вопросы по тегам