Кирилл Данилов (donz_ru) wrote,
Кирилл Данилов
donz_ru

Category:

Осторожно, ZK

Задача: есть класс
public class MyObj
{
  private boolean isEnable;
  private boolean check1;
  private BigDecimal money1;
  private BigDecimal money2;
  private Boolean check2;
}
Есть соответствующие им геттеры и сеттеры.
На странице в браузере должен быть список таких объектов в виде таблицы. В строках таблицы располагаются чекбоксы и инпуты для редактирования вышеперечисленных полей класса. При этом последние четыре поля должны быть доступны для редактирования (быть активными, проще говоря, незадизабленными), только если первое поле true, то есть стоит галочка в чекбоксе конкретной строки.
Соответственно, управлять активностью этих элементов надо при каждом изменении инпута, отвечающего за первое поле, плюс сразу после загрузки страницы, чтобы отобразить изначальное состояние. Первое, что приходит в голову - это доступ к полям объекта через ${someVar} для инициализации состояния контролов сразу после загрузки страницы, и яваскрипт на изменение контрола, замапленного на первое поле объекта.
Таблица на ZK, графические компоненты тоже из этого фреймворка. Если с яваскриптом, который отлавливает изменение состояния контрола, кое как через жопу справился (используются конструкции типа #{self.parent.parent.parent} - это я передаю компонент строки, в котором ищу все контролы, кроме первого, и дизаблю их, если надо, не забывая на лету править стили). То со вторым конкретно впал в ступор. На форуме проекта нашел, как сделать вызов яваскрипта после загрузки страницы, но использовать это нельзя как минимум из-за невозможности передачи динамических параметров. Во-вторых, из-за чрезмерной даже для ZK кривости.
Создал отдельную тему по вопросу, что же делать с инициализацией начального состояния контролов. Более менее приличного ответа нет. Только намек на то, чтобы все делать через сервер. То есть, кликнули на контрол - ушел запрос на сервер. Контрол потерял фокус - ушел запрос на сервер и т.д. Сегодня плюнул и сделал именно так. Через их интерфейс TypeConverter, который по идее создавался для удобного преобразования переданных в страницу данных. У меня же получилось полное уродство, "стучащая" на сервер на каждый onchange каждого контрола (строк на странице около сотни):

import org.zkoss.zkplus.databind.TypeConverter;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.api.HtmlBasedComponent;
import org.zkoss.zul.impl.api.InputElement;
import org.zkoss.zul.api.Checkbox;
import utils.CommonUtils;

import java.lang.reflect.Method;

/**
 * В зависимости от значения поля isEnable объекта MyObj делает компонент инабленным или задизабленным.
 * Если true, то компонент будет инбаленным, если false, то задизабленным.
 * Сделано, потому что херов ZK не поддерживает выражения при динамическом биндинге.
 * Также надо учитывать, что на каждое изменение значения флага будет запрос на сервер
 *
 * @author danilov
 */
public class DisableTypeConverter implements TypeConverter
{
    /**
     * Название аттрибута, показывающего, нужно ли дизаблить компонент
     */
    public static final String FLAG_ATTR = "enabled";

    /**
     * Название аттрибута, в котором будет храниться изначальный объект, чтобы потом можно было бы его вернуть
     */
    public static final String SAVE_ATTR = "saveValue";

    /**
     * Суффикс, который нужно приставить к стилю, чтобы элемент выглядел задизабленным
     */
    public static final String DISABLED_SUFFIX = "_disabled";

    /**
     * Название аттрибута, в котором будет передано название поле, которое надо обслужить этим конвертером
     */
    public static final String FIELD_ATTR = "field";

    @Override
    public Object coerceToUi( Object val, Component comp )
    {
        MyObj myObj = ( MyObj ) val;
        boolean isBooleanType = false;
        if( myObj != null )
        {
            HtmlBasedComponent htmlComp = ( ( HtmlBasedComponent ) comp );
            if( htmlComp instanceof Checkbox )
            {
                isBooleanType = true;
            }
            if( !myObj.isEnable() )
            {
                //Ну не пиздец ли это???
                if( htmlComp instanceof Checkbox )
                {
                    ( ( Checkbox )htmlComp ).setDisabled( true );

                }
                else
                {
                    ( ( InputElement ) htmlComp ).setDisabled( true );
                }
                htmlComp.setSclass( htmlComp.getSclass() + DISABLED_SUFFIX );
            }
            htmlComp.setAttribute( SAVE_ATTR, myObj );
            String fieldName = ( String )comp.getAttribute( FIELD_ATTR );
            //Если поле не указано, значит надо указать, так как без него работать не будет, а потому на исключение забиваем
            try
            {
                Method method;
                try
                {
                    method = myObj.getClass().getMethod( CommonUtils.convertFieldNameToGetMethodName( fieldName, isBooleanType ) );
                }
                catch( NoSuchMethodException e )
                {
                    //видимо, булевский тип определили неправильно, так что попробуем еще раз
                    method = myObj.getClass().getMethod( CommonUtils.convertFieldNameToGetMethodName( fieldName, !isBooleanType ) );
                }
                return method.invoke( myObj );
            }
            catch( Exception ignored )
            {
                throw new RuntimeException( "Give me the right field name! Wrong field name: " + fieldName );
            }
        }
        return myObj;
    }

    @Override
    public Object coerceToBean( Object val, Component comp )
    {
        MyObj myObj = ( MyObj )comp.getAttribute( SAVE_ATTR );
        String fieldName = ( String )comp.getAttribute( FIELD_ATTR );
        //Если поле не указано, значит надо указать, так как без него работать не будет
        try
        {
            Method method;
            try
            {
                method = myObj.getClass().getMethod( CommonUtils.convertFieldNameToSetMethodName( fieldName ), val.getClass() );
            }
            catch( NoSuchMethodException e )
            {
                //видимо, булевский тип определили неправильно, так что попробуем еще раз
                method = myObj.getClass().getMethod( CommonUtils.convertFieldNameToSetMethodName( fieldName ), boolean.class );
            }
            method.invoke( myObj, val );
        }
        catch( Exception ignored )
        {
            throw new RuntimeException( "Give me the right field name! Wrong field name: " + fieldName );
        }
        return myObj;
    }
}

Рефлексию прикрутил, чтобы не плодить четыре конвертера по одному на каждое поле. Хотя тут скорее всего ошибся, надо было слабать четыре. Точно получилось бы быстрее. А универсальности все равно нет - жесткая завязка на конкретный класс.
Работает. Точнее, будет работать, когда верстальщик переопределит стандартные стили ZK для задизабленых компонентов. А также есть мнение, что будет мигать после загрузки, то есть все контролы сначала будут активными, а потом по мере отрабатывания маппинга будут дизаблиться, если это нужно. Как сделать по-другому - хз, возможно, что никак.
Зато идеологически правильно - все же данные хранятся на сервере, значит изменять их можно только через сервер. Наверное, как-то так думали создатели этого нереального геморроя.
Думаю, я терпеливый человек, раз столько времени пытался найти нормальное решение, понял, что его нет, написал этот говнокод и при этом не расхуячил монитор и клавиатуру. Душевно спокойнее будет суп в дуршлаге сварить.
Или все-таки кто-нибудь может объяснить, в чем я не прав, за исключением использования рефлексии?
Пусть будет проклят тот день, когда я согласился использовать это чудовище ради одной красивой таблички...



Tags: it, java, zkoss
Subscribe

  • Новость-молния из Дахаба!

    У съема квартиры в Дахабе есть и минусы - сижу и жду, когда починят участок говнопровода между моим и нижним этажом. Зато наконец появилось время на…

  • Пересмотр приоритетов в логистике

    В рамках начала подведения итогов года. У меня с ковидом нормально так изменилось отношение к удобству в транспорте. Раньше на…

  • Мотивация выучить иностранный язык

    Тут на одном форуме сделали анализ лексикона пользователей. Мой словарный запас по итогам последних 420 комментариев около пяти тысяч слов. Так как…

  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 0 comments