Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation does not show Visualization in TableView (TextFieldTableCell) #546

Open
Siedlerchr opened this issue Apr 1, 2018 · 5 comments
Milestone

Comments

@Siedlerchr
Copy link

I have created a very simple TableView with two columns and added a ValidiationVisualizer to the TextFieldTableCell. The validation triggers correctly, but there is no visual clue shown.
I tried different Decorators etc. but I could not figure it out. Any help would be appreciated!

grafik

This is the code I used to setup the table:

private final ControlsFxVisualizer visualizer = new ControlsFxVisualizer();

        colContent.setCellFactory(column -> {

            TextFieldTableCell<StringViewModel, String> cell = new TextFieldTableCell<>(new DefaultStringConverter());
            column.setCellValueFactory(cellData -> {

                visualizer.initVisualization(cellData.getValue().contentValidation(), cell);
                return cellData.getValue().getContent();
            });
            return cell;

        });
        colContent.setOnEditCommit((CellEditEvent<StringViewModel, String> cell) -> {
                                       cell.getRowValue().setContent(cell.getNewValue());
                                   });

        tblStrings.itemsProperty().bindBidirectional(viewModel.allStringsProperty());
        tblStrings.setEditable(true);

https://github.com/JabRef/jabref/pull/3735/files#diff-117aa7fc99c0b7380bdaf11022bb3575

In my ViewModel I have the Validator:

 private final Function<String, ValidationMessage> function = input -> {
        if (input == null) {
            System.out.println("Null checker");
            return ValidationMessage.error("May not be null2");
        } else if (input.trim().isEmpty()) {
            System.out.println("Should not empty");
            return ValidationMessage.warning("Should not be empty");
        } else {
            return null; // everything is ok
        }
    };

    public StringViewModel(String label, String content) {

        this.label.setValue(label);
        this.content.setValue(content);

        labelValidator = new FunctionBasedValidator<>(this.label, function);
        contentValidator = new FunctionBasedValidator<>(this.content, function);
    }

https://github.com/JabRef/jabref/pull/3735/files#diff-df16f2f707ba7bfd296b444ac7962c20

@manuel-mauky
Copy link
Collaborator

Your validator setup looks good but I think the setup of the cellfactory is not correct.

You are initializing the visualizer for one cell-value and one specific cell. However, JavaFX is reusing cells for different values. It will only create as many cells as needed for one view of elements in the table and it will reuse them when the user scrolls down.

The setup of a cellFactory and cellValueFactory is unconnected in JavaFX. The cellValueFactory only defines how a value to display is resolved. The cellFactory defines which type of cells is used. However, the cellFactory doesn't know anything about actual values.
Your setup is only possible because you are nesting both factory methods. Normally you don't define the cellValueFactory inside a cellFactory method.

Ok, so how to fix this? I have to admit that I haven't used the validation within a TableView yet. The basic assumption of the validation module is that there is a one-to-one connection between a value and a control in the view (which is typically the case for forms). This assumption isn't true for Tables so we have to find a workaround.

In TableCell there is a method updateItem which is invoked everytime the value of a cell is updated.
A typicall pattern is to create a subclass (can be anonymous inner class) and override this method. This would probably be the correct place to update the validation but I don't know hot to access the actual value (the StringViewModel instance in your example) inside of this method. And I don't know if this would create any sideeffects.

I don't have a ready-to-use solution but I'm trying to find one.

@manuel-mauky
Copy link
Collaborator

I found a solution that seems to work but I haven't fully tested it and I don't know if there are any negative side-effects. But you could try it out:

colContent.setCellValueFactory(cellData -> cellData.getValue.getContent());

colContent.setCellFactory(column -> new TextFieldTableCell<StringViewModel, String>(new DefaultStringConverter()) {
	@Override
	public void updateItem(String item, boolean empty) {
		super.updateItem(item, empty);

		if(! empty) {
			if(getTableRow() != null) {
				Object rowItem = getTableRow().getItem();

				if(rowItem != null && rowItem instanceof StringViewModel) {
					StringViewModel vm = (StringViewModel) rowItem;

					visualizer.initVisualization(vm.contentValidation(), this);
				}
			}
		}
	}
});

With this setup I get the validation marks in the table cells. However, the default styling of ControlsFX seems to be not optimal for this usecase. Due to the fact that TextFieldTableCell renders a Label when the value was entered, there is no red border but only a small mark at the buttom left corner of the TableCell.
grafik

Another limitation is that it doesn't validate the inital values but only after the user has changed the value in the table.

@Siedlerchr
Copy link
Author

Yeah cool! Thanks for the help, I will try this out. In JabRef we have a custom icon decorater that hopefully looks better.

@Siedlerchr
Copy link
Author

The only odd effect I encounter is the following:
Clear the first column cell
-> icon shows
Clear the second column cell
-> The first icon is gone , the icon shows for the second cell
But, when I now click the button to enter a new row, then both icons are shown again.
Seems like a redraw is then triggered.

grafik

@manuel-mauky
Copy link
Collaborator

I don't know the internals of how and when re-render is done in JavaFX TableViews.
Maybe there are examples or documentation of how the ControlsFX validation can be used with Tables? The visualization part of mvvmFX validation is quite similar to the one from ControlsFX, just with a different API. So maybe if they have any hints on how this works with pure ControlsFX then we probably can adapt the solution.

@manuel-mauky manuel-mauky added this to the 1.9.0 milestone Dec 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants