Форматирование редактируемой ячейки таблицы

Добрый день.

У меня существует атрибут сущности Ставка. У него NamePattern = “#%”. Данный атрибут выводится в редактируемой таблице верно (если значение 0.2, то выводится 20%). Однако при редактировании ячейки таблицы и вводе числа без знака процента появляется ошибка - Неверный тип значения. Как сделать, чтобы знак процента подставлялся автоматически?

Здравствуйте!

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

Примерно будет выглядеть вот так:

@Inject
private GroupTable<Test> rateTable;
@Inject
private ComponentsFactory factory;

@Override
public void init(Map<String, Object> params) {
    rateTable.addGeneratedColumn("rate", entity -> {
        TextField textField = (TextField) factory.createComponent(TextField.NAME);
        textField.setDatasource(rateTable.getItemDatasource(entity), "rate");
        textField.setDatatype(customDatatype);
        return textField;
    });
}

Как создать собственный Datatype можно посмотреть в документации.
Как использовать генерируемые колонки: документация.

Добрый день, Роман.

Спасибо за ответ.

Сделал Datatype:

package com.ekom.ekomerp;

import com.google.common.base.Strings;
import com.haulmont.chile.core.annotations.JavaClass;
import com.haulmont.chile.core.datatypes.Datatype;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Locale;

@JavaClass(BigDecimal.class)
public class PercentageDatatype implements Datatype<BigDecimal> {

    private static final String PATTERN = "###,###.####%";

    @Override
    public String format(@Nullable Object value) {
        if (value == null)
            return "";
        DecimalFormat format = new DecimalFormat(PATTERN);
        return format.format(value);

    }

    @Override
    public String format(@Nullable Object value, Locale locale) {
        return format(value);
    }

    @Nullable
    @Override
    public BigDecimal parse(@Nullable String value) throws ParseException {
        if (Strings.isNullOrEmpty(value))
            return null;
        DecimalFormat format = new DecimalFormat(PATTERN);
        BigDecimal taxRate = BigDecimal.valueOf(format.parse(value).doubleValue());
        return taxRate;
    }

    @Nullable
    @Override
    public BigDecimal parse(@Nullable String value, Locale locale) throws ParseException {
        return parse(value);
    }
}

Зарегистрировал в metadata.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<metadata xmlns="http://schemas.haulmont.com/cuba/metadata.xsd">
    <datatypes>
        <datatype id="fourDigitsScaleBigDecimal"
                  class="com.ekom.ekomerp.FourDigitsScaleBigDecimal"
                  decimalSeparator="."
                  format="#0.0000"
                  groupingSeparator=""/>
        <datatype id="twoDigitsScaleBigDecimal"
                  class="com.ekom.ekomerp.TwoDigitsScaleBigDecimal"
                  decimalSeparator="."
                  format="#0.00"
                  groupingSeparator=""/>
        <datatype id="fourDigitsScaleDouble"
                  class="com.ekom.ekomerp.FourDigitsScaleDouble"
                  decimalSeparator="."
                  format="#0.0000"
                  groupingSeparator=""/>
        <datatype id="percent" class="com.ekom.ekomerp.PercentageDatatype"/>
    </datatypes>
    <metadata-model namespace="ekomerp"
                    root-package="com.ekom.ekomerp">
        <class>com.ekom.ekomerp.entity.SpecificationTreeNode</class>
        <class>com.ekom.ekomerp.entity.WeekTaskEntry</class>
    </metadata-model>
</metadata>

Создал генерируемую колонку:

    @Inject
    private PercentageDatatype percentageDatatype;

public void init(Map<String, Object> params) {
        purchaseOrderLineTable.addGeneratedColumn("taxRate",entity -> {
            TextField textField = (TextField) factory.createComponent(TextField.NAME);
            textField.setEditable(true);
            textField.setDatasource(purchaseOrderLineTable.getItemDatasource(entity), "taxRate");
            textField.setDatatype(percentageDatatype);
            return textField;
        });
}

Форматирование не применяется. В чем может быть проблема?

1 симпатия

В случае с datatype @Inject не работает и percentageDatatype не проинициализирован.
Получить экземпляр Datatype можно через DatatypeRegistry:

@Inject
private DatatypeRegistry datatypeRegistry;

@Override
public void init(Map<String, Object> params) {
    PercentageDatatype percentageDatatype = (PercentageDatatype) datatypeRegistry.get("percent");
}
1 симпатия

В этом случае получаю аналогичный результат. Datatype не применяется.

Вы не могли бы воспроизвести эту проблему в пустом проекте? Очень сложно обсуждать проблему, которую мы не можем увидеть вживую.

Присоединяю проект.

Необходимо, чтобы в таблицу и подгружался процентный вид. И при изменении устанавливался.newProject.rar (330,8 КБ)

PersentageDatatype применяется. Если попробуете подебажить, то увидите, что его методы отрабатываются. Ваш паттерн ожидает, что в конце строки должен быть символ %, поэтому в методе parse() необходимо проверять наличие данного символа:

@Nullable
@Override
public BigDecimal parse(@Nullable String value) throws ParseException {
    if (Strings.isNullOrEmpty(value))
        return null;
    DecimalFormat format = new DecimalFormat(PATTERN);
    BigDecimal taxRate;
    if (!value.endsWith("%")) {
        taxRate = BigDecimal.valueOf(format.parse(value + "%").doubleValue());
    } else {
        taxRate = BigDecimal.valueOf(format.parse(value).doubleValue());
    }
    return taxRate;
}

Также выяснилось, что существует баг с выводом форматированного значения в текстовое поле в генерируемой колонке, я завёл issue в GitHub репозитории: cuba-platform/cuba#1957.

Спасибо. Жду исправления.

Во время решения задачи выяснилось, что проблема была в не совсем корректно реализованном PercentageDatatype.
Вы можете попробовать более простой и учитывающий текущую локализацию Datatype:

@JavaClass(BigDecimal.class)
public class PercentageDatatype extends BigDecimalDatatype {

    public PercentageDatatype(Element element) {
        super(element);
    }

    @Override
    public String format(Object value) {
        String format = super.format(value);
        return Strings.isNullOrEmpty(format) ? format : format + "%";
    }

    @Override
    public String format(Object value, Locale locale) {
        String format = super.format(value, locale);
        return Strings.isNullOrEmpty(format) ? format : format + "%";
    }

    @Override
    public BigDecimal parse(String value) throws ParseException {
        if (!Strings.isNullOrEmpty(value)) {
            value = value.replace("%", "");
        }

        return super.parse(value);
    }

    @Override
    public BigDecimal parse(String value, Locale locale) throws ParseException {
        if (!Strings.isNullOrEmpty(value)) {
            value = value.replace("%", "");
        }

        return super.parse(value, locale);
    }
}
2 симпатии

Спасибо, помогло. Но я немного подправил учитывая работы с процентами.

@JavaClass(BigDecimal.class)
public class PercentageDatatype extends BigDecimalDatatype {

    public PercentageDatatype(Element element) {
        super(element);
    }

    @Override
    public String format(Object value) {
        if (value instanceof Object) {
            BigDecimal decimal = (BigDecimal) value;
            value = decimal.multiply(new BigDecimal(100));
        }
        String format = super.format(value);
        return Strings.isNullOrEmpty(format) ? format : format + "%";
    }

    @Override
    public String format(Object value, Locale locale) {
        if (value instanceof Object) {
            BigDecimal decimal = (BigDecimal) value;
            value = decimal.multiply(new BigDecimal(100));
        }
        String format = super.format(value, locale);
        return Strings.isNullOrEmpty(format) ? format : format + "%";
    }

    @Override
    public BigDecimal parse(String value) throws ParseException {
        if (!Strings.isNullOrEmpty(value)) {
            value = value.replace("%", "");
        }

        return super.parse(value).divide(new BigDecimal(100));
    }

    @Override
    public BigDecimal parse(String value, Locale locale) throws ParseException {
        if (!Strings.isNullOrEmpty(value)) {
            value = value.replace("%", "");
        }

        return  super.parse(value).divide(new BigDecimal(100));
    }
}

Думаю имеет смысл создать стандартный DataType для работы с процентами.
Также, считаю, что необходимо Ваши корректировки внести в документацию, так как предыдущий пример я делал по ней:
https://doc.cuba-platform.com/manual-7.0-ru/datatype_custom_example.html

2 симпатии