Я пытаюсь выяснить, как связать метаданные об объекте, который не является фактическим полем в объекте. Например, рассмотрим следующее:
Task:
title: string
description: string
...
notes: List<TaskNote>
Я хотел бы иметь возможность отображать Task в сетке или в форме «редактора» и отображать, скажем, количество TaskNote, связанных с Task. Итак, в данном случае подсчет — это просто количество элементов в списке notes.
Как создать привязку данных для чего-то подобного? Каковы лучшие практики/методы?
Я еще ничего не пробовал. Я спрашиваю, как лучше всего это сделать.
Да, ваш вопрос слишком открытый, чтобы квалифицировать ответ в соответствии с кодексом поведения StackOverflow. Например. вы хотите связать список задач или просто задачу. Тем не менее, на страницах Vaadin есть хороший набор учебных материалов, см. vaadin.com/learn/training/v14-forms и vaadin.com/learn/training/v14-grid





Связующее используется для привязки Fields к определенному bean-компоненту Properties. Боб Person может иметь Properties «имя», «фамилию» и «год рождения». Однако Field и Property — довольно абстрактные понятия, оставляющие вам много места для маневра.
Field — это нечто, обычно Component, реализующее интерфейс HasValue. Интерфейс HasValue (и, следовательно, Field) имеет тип Java, такой как String.Property по сути является парой геттер/сеттер. Пара должна иметь совпадающий тип — возвращаемый тип геттера должен совпадать с тем, что установщик принимает в качестве входных данных.
Property может быть предоставлен путем реализации функционального интерфейса ValueProvider, который в простой форме может быть лямбда-выражением, таким как person -> person.getFirstName()Setter. Простым примером лямбды может быть (person, newFirstName) -> person.setFirstName(newFirstName).Когда вы создаете Binder, вы тем или иным образом предоставляете ValueProvider и Setter; наиболее явным способом было бы, когда вы привязываетесь следующим образом:
Binder<Person> binder = new Binder<>();
TextField firstNameField = new TextField("First name");
binder.forField(firstNameField).bind(valueProvider, setter);
Когда вы имеете дело с чем-то, что является частью компонента, но не является Property, у вас есть несколько вариантов. Например, «возраст» человека можно рассчитать как функцию его «года рождения». Допустим, мы хотим иметь возможность редактировать только год рождения, но мы также хотим отображать возраст как значение, доступное только для чтения. Это также может быть процент скидки в зависимости от возраста или чего-то еще.
Первый, самый простой подход — использовать Field, но только с ValueProvider и без Setter. Таким образом, у вас может быть что-то вроде этого (используя TextField для простоты):
Binder<Person> binder = new Binder<>();
TextField ageField = new TextField("Age (calculated)");
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
Недостатком этого подхода является то, что вы все еще используете TextField, который является компонентом редактора. Вы можете установить его в режим только для чтения, чтобы пользователь не мог изменить значение, но все же похоже, что в какой-то момент вы сможете его отредактировать.
Еще одна вещь, которую вы можете использовать, — это ReadOnlyHasValue, который представляет собой интерфейс для создания Property без Setter. Это немного неуклюже, но вы можете использовать его с привязкой:
Span ageDisplay = new Span();
ReadOnlyHasValue<String> ageField = new ReadOnlyHasValue<>(text -> ageDisplay.setText(text));
binder.forField(ageField).bind(person -> calculateAge(person.getBirthYear()), null);
Наконец, поскольку ValueProvider и Setter — это всего лишь интерфейсы Java, вы можете реализовать их с любыми побочными эффектами, которые вам могут понадобиться. Таким образом, вы можете связать любую логику с изменениями свойства:
Binder<Person> binder = new Binder<>();
Span ageDisplay = new Span();
TextField birthYear = new TextField();
binder.forField(birthYear)
.bind(person -> { // ValueProvider
return person.getBirthYear();
}, (person, newBirthYear) -> { // Setter
person.setBirthYear(newBirthYear);
ageDisplay.setText(calculateAge(newBirthYear));
});
Благодарю вас за эту информацию. Я попробую некоторые из этих предложений и дам вам знать, как это происходит.
Пожалуйста, добавьте код, который вы пробовали, и причину сбоя (например, ошибки, трассировки стека, журналы и т. д.), чтобы мы могли его улучшить.