You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

datamodel-forms.asciidoc 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. ---
  2. title: Binding Data to Forms
  3. order: 3
  4. layout: page
  5. ---
  6. [[datamodel.forms]]
  7. = Binding Data to Forms
  8. A typical application lets the user fill out structured data and maybe also browse previously entered data.
  9. The data that is being entered is typically represented in code as an instance of a business object (bean), for instance a [classname]#Person# in an HR application.
  10. Vaadin Framework provides a [classname]#Binder# class that the developer can use to define how the values in a business object should be bound to the fields shown in the user interface.
  11. [classname]#Binder# takes care of reading values from the business object and converting the user's data between the format expected by the business object and the format expected by the field.
  12. The input entered by the user can also be validated, and the current validation status can be presented to the user in different ways.
  13. TIP: Watch the https://vaadin.com/training/course/view/data-binding-and-validation[Vaadin 8: Data binding and validation] free training video to learn more about creating simple and complex forms, value conversion and validation, and how the Vaadin data model works.
  14. The first step to binding fields for a form is to create a [classname]#Binder# and bind some input fields. There is only one [classname]#Binder# instance for each form and it is used for all fields in that form.
  15. [source, java]
  16. ----
  17. Binder<Person> binder = new Binder<>();
  18. TextField titleField = new TextField();
  19. // Start by defining the Field instance to use
  20. binder.forField(titleField)
  21. // Finalize by doing the actual binding to the Person class
  22. .bind(
  23. // Callback that loads the title from a person instance
  24. Person::getTitle,
  25. // Callback that saves the title in a person instance
  26. Person::setTitle));
  27. TextField nameField = new TextField();
  28. // Shorthand for cases without extra configuration
  29. binder.bind(nameField, Person::getName, Person::setName);
  30. ----
  31. When we have bound field components using our binder, we can use the binder to load values from a person into the field, let the user edit the values and finally save the values back into a person instance.
  32. [source, java]
  33. ----
  34. // The person to edit
  35. // Would be loaded from the backend in a real application
  36. Person person = new Person("John Doe", 1957);
  37. // Updates the value in each bound field component
  38. binder.readBean(person);
  39. Button saveButton = new Button("Save",
  40. event -> {
  41. try {
  42. binder.writeBean(person);
  43. // A real application would also save the updated person
  44. // using the application's backend
  45. } catch (ValidationException e) {
  46. Notification.show("Person could not be saved, " +
  47. "please check error messages for each field.");
  48. }
  49. });
  50. // Updates the fields again with the previously saved values
  51. Button resetButton = new Button("Reset",
  52. event -> binder.readBean(person));
  53. ----
  54. With these basic steps, we have defined everything that is needed for loading, editing and saving values for a form.
  55. The above example uses Java 8 method references for defining how field values are loaded and saved. It is also possible to use a lambda expression or an explicit instance of the callback interface instead of a method reference.
  56. [source, java]
  57. ----
  58. // With lambda expressions
  59. binder.bind(titleField,
  60. person -> person.getTitle(),
  61. (person, title) -> person.setTitle(title));
  62. // With explicit callback interface instances
  63. binder.bind(nameField,
  64. new ValueProvider<Person, String>() {
  65. @Override
  66. public String apply(Person person) {
  67. return person.getName();
  68. }
  69. },
  70. new Setter<Person, String>() {
  71. @Override
  72. public void accept(Person person, String name) {
  73. person.setName(name);
  74. }
  75. });
  76. ----
  77. == Binding non-modifiable Data
  78. Non-modifiable data can be also bound to any component or component property with [classname]#ReadOnlyHasValue# helper class.
  79. For example, `Panel` caption can display a person full name:
  80. [source, java]
  81. ----
  82. Panel infoPanel = new Panel();
  83. ReadOnlyHasValue<Person> panelTitle = new ReadOnlyHasValue<>(
  84. person -> infoPanel.setCaption(person.getLastName() + ", " + person.getFirstName()));
  85. binder.forField(panelTitle).bind(person -> person, null);
  86. ----
  87. == Validating and Converting User Input
  88. `Binder` supports checking the validity of the user's input and converting the values between the type used in business objects and the bound UI components.
  89. These two concepts go hand in hand since validation can be based on a converted value, and being able to convert a value is a kind of validation.
  90. [[datamodel.forms.validation]]
  91. === Validation
  92. An application typically has some restrictions on exactly what kinds of values the user is allowed to enter into different fields.
  93. [classname]#Binder# lets us define validators for each field that we are binding.
  94. The validator is by default run whenever the user changes the value of a field, and the validation status is also checked again when saving.
  95. Validators for a field are defined between the [methodname]#forField# and [methodname]#bind# steps when a binding is created.
  96. A validator can be defined using a [classname]#Validator# instance or inline using a lambda expression.
  97. [source, java]
  98. ----
  99. binder.forField(emailField)
  100. // Explicit validator instance
  101. .withValidator(new EmailValidator(
  102. "This doesn't look like a valid email address"))
  103. .bind(Person::getEmail, Person::setEmail);
  104. binder.forField(nameField)
  105. // Validator defined based on a lambda and an error message
  106. .withValidator(
  107. name -> name.length() >= 3,
  108. "Full name must contain at least three characters")
  109. .bind(Person::getName, Person::setName);
  110. binder.forField(titleField)
  111. // Shorthand for requiring the field to be non-empty
  112. // This is conditional on Binding::setAsRequiredEnabled(boolean)
  113. .asRequired("Every employee must have a title")
  114. .bind(Person::getTitle, Person::setTitle);
  115. ----
  116. [NOTE]
  117. [classname]#Binder#.[methodname]#forField# works like a builder where [methodname]#forField# starts the process, is followed by various configuration calls for the field and [methodname]#bind# acts as the finalizing method which applies the configuration.
  118. The validation state of each field is updated whenever the user modifies the value of that field.
  119. The validation state is by default shown using [classname]#AbstractComponent#.[methodname]#setComponentError# which is used by the layout that the field is shown in. Whenever an error is set, the component will also get a `v-<component>-error` class name, e.g. `v-textfield-error`. This error class will by default add a red border on the component. The component will also get a tooltip that shows the error message text.
  120. We can also customize the way a binder displays error messages to get more flexibility than what [methodname]#setComponentError# provides.
  121. The easiest way of customizing this is to configure each binding to use its own [classname]#Label# that is used to show the status for each field.
  122. [NOTE]
  123. The status label is not only used for validation errors but also for showing confirmation and helper messages.
  124. [source, java]
  125. ----
  126. Label emailStatus = new Label();
  127. binder.forField(emailField)
  128. .withValidator(new EmailValidator(
  129. "This doesn't look like a valid email address"))
  130. // Shorthand that updates the label based on the status
  131. .withStatusLabel(emailStatus)
  132. .bind(Person::getEmail, Person::setEmail);
  133. Label nameStatus = new Label();
  134. binder.forField(nameField)
  135. // Define the validator
  136. .withValidator(
  137. name -> name.length() >= 3,
  138. "Full name must contain at least three characters")
  139. // Define how the validation status is displayed
  140. .withValidationStatusHandler(status -> {
  141. nameStatus.setValue(status.getMessage().orElse(""));
  142. nameStatus.setVisible(status.isError());
  143. })
  144. // Finalize the binding
  145. .bind(Person::getName, Person::setName);
  146. ----
  147. It is possible to add multiple validators for the same binding.
  148. The following example will first validate that the entered text looks like an email address, and only for seemingly valid email addresses it will continue checking that the email address is for the expected domain.
  149. [source, java]
  150. ----
  151. binder.forField(emailField)
  152. .withValidator(new EmailValidator(
  153. "This doesn't look like a valid email address"))
  154. .withValidator(
  155. email -> email.endsWith("@acme.com"),
  156. "Only acme.com email addresses are allowed")
  157. .bind(Person::getEmail, Person::setEmail);
  158. ----
  159. In some cases, the validation of one field depends on the value of some other field.
  160. We can save the binding to a local variable and trigger a revalidation when another field fires a value change event.
  161. [source, java]
  162. ----
  163. Binder<Trip> binder = new Binder<>();
  164. DateField departing = new DateField("Departing");
  165. DateField returning = new DateField("Returning");
  166. // Store return date binding so we can revalidate it later
  167. Binder.BindingBuilder<Trip, LocalDate> returnBindingBuilder = binder.forField(returning)
  168. .withValidator(returnDate -> !returnDate.isBefore(departing.getValue()),
  169. "Cannot return before departing");
  170. Binder.Binding<Trip, LocalDate> returnBinder = returnBindingBuilder.bind(Trip::getReturnDate, Trip::setReturnDate);
  171. // Revalidate return date when departure date changes
  172. departing.addValueChangeListener(event -> returnBinder.validate());
  173. ----
  174. [[datamodel.forms.conversion]]
  175. === Conversion
  176. You can also bind application data to a UI field component even though the types do not match.
  177. In some cases, there might be types specific for the application, such as custom type that encapsulates a postal code that the user enters through a [classname]#TextField#.
  178. Another quite typical case is for entering integer numbers using a [classname]#TextField# or a [classname]#Slider#.
  179. Similarly to validators, we can define a converter using a [classname]#Converter# instance or inline using lambda expressions. We can optionally specify also an error message.
  180. [source, java]
  181. ----
  182. TextField yearOfBirthField = new TextField("Year of birth");
  183. binder.forField(yearOfBirthField)
  184. .withConverter(
  185. new StringToIntegerConverter("Must enter a number"))
  186. .bind(Person::getYearOfBirth, Person::setYearOfBirth);
  187. // Slider for integers between 1 and 10
  188. Slider salaryLevelField = new Slider("Salary level", 1, 10);
  189. binder.forField(salaryLevelField)
  190. .withConverter(Double::intValue, Integer::doubleValue)
  191. .bind(Person::getSalaryLevel, Person::setSalaryLevel);
  192. ----
  193. Multiple validators and converters can be used for building one binding.
  194. Each validator or converter is used in the order they were defined for a value provided by the user.
  195. The value is passed along until a final converted value is stored in the business object, or until the first validation error or impossible conversion is encountered.
  196. When updating the UI components, values from the business object are passed through each converter in the reverse order without doing any validation.
  197. [NOTE]
  198. A converter can be used as a validator but for code clarity and to avoid boilerplate code, you should use a validator when checking the contents and a converter when modifying the value.
  199. [source, java]
  200. ----
  201. binder.forField(yearOfBirthField)
  202. // Validator will be run with the String value of the field
  203. .withValidator(text -> text.length() == 4,
  204. "Doesn't look like a year")
  205. // Converter will only be run for strings with 4 characters
  206. .withConverter(
  207. new StringToIntegerConverter("Must enter a number"))
  208. // Validator will be run with the converted value
  209. .withValidator(year -> year >= 1900 && year < 2000,
  210. "Person must be born in the 20th century")
  211. .bind(Person::getYearOfBirth, Person::setYearOfBirth);
  212. ----
  213. You can define your own conversion either by using callbacks, typically lambda expressions or method references, or by implementing the `Converter` interface.
  214. When using callbacks, there is one for converting in each direction. If the callback used for converting the user-provided value throws an unchecked exception, then the field will be marked as invalid and the message of the exception will be used as the validation error message.
  215. Messages in Java runtime exceptions are typically written with developers in mind and might not be suitable to show to end users.
  216. We can provide a custom error message that is used whenever the conversion throws an unchecked exception.
  217. [source, java]
  218. ----
  219. binder.forField(yearOfBirthField)
  220. .withConverter(
  221. Integer::valueOf,
  222. String::valueOf,
  223. // Text to use instead of the NumberFormatException message
  224. "Please enter a number")
  225. .bind(Person::getYearOfBirth, Person::setYearOfBirth);
  226. ----
  227. There are two separate methods to implement in the `Converter` interface.
  228. `convertToModel` receives a value that originates from the user. The method should return a `Result` that either contains a converted value or a conversion error message.
  229. `convertToPresentation` receives a value that originates from the business object.
  230. Since it is assumed that the business object only contains valid values, this method directly returns the converted value.
  231. [source, java]
  232. ----
  233. class MyConverter implements Converter<String, Integer> {
  234. @Override
  235. public Result<Integer> convertToModel(String fieldValue, ValueContext context) {
  236. // Produces a converted value or an error
  237. try {
  238. // ok is a static helper method that creates a Result
  239. return Result.ok(Integer.valueOf(fieldValue));
  240. } catch (NumberFormatException e) {
  241. // error is a static helper method that creates a Result
  242. return Result.error("Please enter a number");
  243. }
  244. }
  245. @Override
  246. public String convertToPresentation(Integer integer, ValueContext context) {
  247. // Converting to the field type should always succeed,
  248. // so there is no support for returning an error Result.
  249. return String.valueOf(integer);
  250. }
  251. }
  252. // Using the converter
  253. binder.forField(yearOfBirthField)
  254. .withConverter(new MyConverter())
  255. .bind(Person::getYearOfBirth, Person::setYearOfBirth);
  256. ----
  257. The provided `ValueContext` can be used for finding `Locale` to be used for the conversion.
  258. == Loading from and Saving to Business Objects
  259. Once all bindings have been set up, you are ready to actually fill the bound UI components with data from your business object. Changes can be written to the business object automatically or manually.
  260. Writing the changes automatically when the user makes any change through the UI is often the most convenient option, but it might have undesirable side effects – the user may see unsaved changes if some other part of the application uses the same business object instance.
  261. To prevent that, you either need to use a copy of the edited object or use manual writing to only update the object when the user wants to save.
  262. === Manual Reading and Writing
  263. The `readBean` method reads values from a business object instance into the UI components.
  264. [source, java]
  265. ----
  266. Person person = new Person("John Doe", 1957);
  267. binder.readBean(person);
  268. ----
  269. Assuming `binder` has already been configured as in previous examples with a `TextField` bound to the name property, this example would show the value "John Doe" in that field.
  270. To avoid showing lots of errors to the user, validation errors are not shown until the user edits each field after the form has been bound or loaded.
  271. Even if the user has not edited a field, all validation errors will be shown if we explicitly validate the form or try to save the values to a business object.
  272. [source, java]
  273. ----
  274. // This will make all current validation errors visible
  275. BinderValidationStatus<Person> status = binder.validate();
  276. if (status.hasErrors()) {
  277. Notification.show("Validation error count: "
  278. + status.getValidationErrors().size());
  279. }
  280. ----
  281. Trying to write the field values to a business object will fail if any of the bound fields has an invalid value.
  282. There are different methods that let us choose how to structure the code for dealing with invalid values.
  283. Handling a checked exception::
  284. +
  285. --
  286. [source, java]
  287. ----
  288. try {
  289. binder.writeBean(person);
  290. MyBackend.updatePersonInDatabase(person);
  291. } catch (ValidationException e) {
  292. Notification.show("Validation error count: "
  293. + e.getValidationErrors().size());
  294. }
  295. ----
  296. --
  297. Checking a return value::
  298. +
  299. --
  300. [source, java]
  301. ----
  302. boolean saved = binder.writeBeanIfValid(person);
  303. if (saved) {
  304. MyBackend.updatePersonInDatabase(person);
  305. } else {
  306. Notification.show("Validation error count: "
  307. + binder.validate().getValidationErrors().size());
  308. }
  309. ----
  310. --
  311. [NOTE]
  312. Note, if you need to write the values passing the validation regardless of having one or more failing validators, you can
  313. use binder.writeBeanAsDraft(person).
  314. Binder keeps track of which bindings have been updated by the user and which bindings are in an invalid state.
  315. It also fires an event when this status changes.
  316. We can use that event to make the save and reset buttons of our forms become enabled or disabled depending on the current status of the form.
  317. [source, java]
  318. ----
  319. binder.addStatusChangeListener(event -> {
  320. boolean isValid = event.getBinder().isValid();
  321. boolean hasChanges = event.getBinder().hasChanges();
  322. saveButton.setEnabled(hasChanges && isValid);
  323. resetButton.setEnabled(hasChanges);
  324. });
  325. ----
  326. === Automatic Saving
  327. Instead of manually saving field values to a business object instance, we can also bind the values directly to an instance.
  328. In this way, `Binder` takes care of automatically saving values from the fields.
  329. [source, java]
  330. ----
  331. Binder<Person> binder = new Binder<>();
  332. // Field binding configuration omitted, it should be done here
  333. Person person = new Person("John Doe", 1957);
  334. // Loads the values from the person instance
  335. // Sets person to be updated when any bound field is updated
  336. binder.setBean(person);
  337. Button saveButton = new Button("Save", event -> {
  338. if (binder.validate().isOk()) {
  339. // person is always up-to-date as long as there are no
  340. // validation errors
  341. MyBackend.updatePersonInDatabase(person);
  342. }
  343. });
  344. ----
  345. [WARNING]
  346. When using the `setBean` method, the business object instance will be updated whenever the user changes the value in any bound field.
  347. If some other part of the application is also using the same instance, then that part might show changes before the user has clicked the save button.
  348. [[datamodel.forms.beans]]
  349. == Binding Beans to Forms
  350. The business objects used in an application are in most cases implemented as Java beans or POJOs.
  351. There is special support for that kind of business object in [classname]#Binder#.
  352. It can use reflection based on bean property names to bind values.
  353. This reduces the amount of code you have to write when binding to fields in the bean.
  354. [source, java]
  355. ----
  356. Binder<Person> binder = new Binder<>(Person.class);
  357. // Bind based on property name
  358. binder.bind(nameField, "name");
  359. // Bind based on sub property path
  360. binder.bind(streetAddressField, "address.street");
  361. // Bind using forField for additional configuration
  362. binder.forField(yearOfBirthField)
  363. .withConverter(
  364. new StringToIntegerConverter("Please enter a number"))
  365. .bind("yearOfBirth");
  366. ----
  367. [NOTE]
  368. Code using strings to identify properties will cause exceptions during runtime if the string contains a typo or if the name of the setter and getter methods have been changed without also updating the string.
  369. If you have a Bean Validation implementation available in your classpath and
  370. want to use JSR 303 Bean Validation annotations then a [classname]#BeanValidationBinder# should be used.
  371. [classname]#BeanValidationBinder# extends [classname]#Binder# class so it has the same API but its implementation
  372. automatically adds a bean validator which takes care of JSR 303 constraints.
  373. Constraints defined for properties in the bean will work in the same way as if configured when the binding is created.
  374. [source, java]
  375. ----
  376. public class Person {
  377. @Max(2000)
  378. private int yearOfBirth;
  379. //Non-standard constraint provided by Hibernate Validator
  380. @NotEmpty
  381. private String name;
  382. // + other fields, constructors, setters, and getters
  383. ...
  384. }
  385. ----
  386. [source, java]
  387. ----
  388. BeanValidationBinder<Person> binder = new BeanValidationBinder<>(Person.class);
  389. binder.bind(nameField, "name");
  390. binder.forField(yearOfBirthField)
  391. .withConverter(
  392. new StringToIntegerConverter("Please enter a number"))
  393. .bind("yearOfBirth");
  394. ----
  395. Constraint annotations can also be defined on the bean level instead of being defined for any specific property.
  396. There are some number of predefined constraint annotations that mark a bound field as required using
  397. [classname]#BeanValidationBinder#.[methodname]#setRequiredIndicatorVisible#. By default [classname]#@NotNull#,
  398. [classname]#@NotEmpty# and [classname]#@Size# (if [methodname]#min()# value is greater than 0)
  399. configures the field as required. It's possible to change this behavior using
  400. the [classname]#BeanValidationBinder#.[methodname]#setRequiredConfigurator# method.
  401. [NOTE]
  402. Bean level validation can only be performed once the bean has been updated. This means that this functionality can only be used together with `setBean`. You need to trigger validation manually if using `readBean` and `writeBean`.
  403. Validation errors caused by that bean level validation might not be directly associated with any field component shown in the user interface, so [classname]#Binder# cannot know where such messages should be displayed.
  404. Similarly to how the [methodname]#withStatusLabel# method can be used for defining where messages for a specific binding should be showed, we can also define a [classname]#Label# that is used for showing status messages that are not related to any specific field.
  405. [source, java]
  406. ----
  407. Label formStatusLabel = new Label();
  408. Binder<Person> binder = new Binder<>(Person.class);
  409. binder.setStatusLabel(formStatusLabel);
  410. // Continue by binding fields
  411. ----
  412. We can also define our own status handler to provide a custom way of handling statuses.
  413. [source, java]
  414. ----
  415. // We will first set the status label's content mode to HTML
  416. // in order to display generated error messages separated by a <br> tag
  417. formStatusLabel.setContentMode(ContentMode.HTML);
  418. BinderValidationStatusHandler<Person> defaultHandler = binder.getValidationStatusHandler();
  419. binder.setValidationStatusHandler(status -> {
  420. // create an error message on failed bean level validations
  421. List<ValidationResult> errors = status.getBeanValidationErrors();
  422. // collect all bean level error messages into a single string,
  423. // separating each message with a <br> tag
  424. String errorMessage = errors.stream().map(ValidationResult::getErrorMessage)
  425. // sanitize the individual error strings to avoid code injection
  426. // since we are displaying the resulting string as HTML
  427. .map(errorString -> Jsoup.clean(errorString, Whitelist.simpleText()))
  428. .collect(Collectors.joining("<br>"));
  429. // finally, display all bean level validation errors in a single label
  430. formStatusLabel.setValue(errorMessage);
  431. formStatusLabel.setVisible(!errorMessage.isEmpty());
  432. // Let the default handler show messages for each field
  433. defaultHandler.statusChange(status);
  434. });
  435. ----
  436. == Using Binder with Declarative Layouts
  437. We can use [classname]#Binder# to connect data to a form that is defined in the declarative format.
  438. This is the design HTML file that we can create using Vaadin Designer:
  439. [source, html]
  440. ----
  441. <vaadin-form-layout size-full>
  442. <vaadin-text-field _id="name"
  443. caption="Name"></vaadin-text-field>
  444. <vaadin-text-field _id="yearOfBirth"
  445. caption="Year of birth"></vaadin-text-field>
  446. <vaadin-button _id="save">
  447. Save
  448. </vaadin-button>
  449. </vaadin-form-layout>
  450. ----
  451. This is the companion Java file that Vaadin Designer creates for us based on the design.
  452. [source, java]
  453. ----
  454. @DesignRoot
  455. @AutoGenerated
  456. public class PersonFormDesign extends FormLayout {
  457. protected TextField name;
  458. protected TextField yearOfBirth;
  459. protected Button save;
  460. public PersonFormDesign() {
  461. Design.read(this);
  462. }
  463. }
  464. ----
  465. Based on those files, we can create a subclass of the design that uses a [classname]#Binder# to automatically connect bean properties to field instances.
  466. This will look at all instance fields that are of a Field type in the class and try to find a bean property with the same name.
  467. [source, java]
  468. ----
  469. public class PersonForm extends PersonFormDesign {
  470. private Binder<Person> binder
  471. = new Binder<>(Person.class);
  472. public PersonForm(Person person) {
  473. binder.bindInstanceFields(this);
  474. binder.readBean(person);
  475. save.addClickListener(event -> {
  476. if (binder.writeBeanIfValid(person)) {
  477. MyBackend.updatePersonInDatabase(person);
  478. }
  479. });
  480. }
  481. }
  482. ----
  483. We can also bind some of the fields before calling [methodname]#bindInstanceFields#.
  484. In this way, fields that require special configuration can still be configured manually while regular fields can be configured automatically.
  485. [source,java]
  486. ----
  487. binder.forField(yearOfBirth)
  488. .withConverter(
  489. new StringToIntegerConverter("Please enter a number"))
  490. .bind(Person::getYearOfBirth, Person::setYearOfBirth));
  491. binder.bindInstanceFields(this);
  492. ----
  493. [NOTE]
  494. If you need to bind nested proprerties with bindInstanceFields method, you need to instantiate the Binder using:
  495. [source,java]
  496. ----
  497. Binder<Person> binder = new Binder<>(Person.class,true);
  498. ----