Home > Mobile >  Label visibility on filtered list search in JavaFX
Label visibility on filtered list search in JavaFX

Time:06-29

I'm using a TextField with a listener to filter a TableView. I've got if statements that set the predicate if newText matches part of the ID or Name.

The search functionality works like I want.

However, my project guidelines require me to also create some form of output upon a search not matching any objects in the TableView.

I created a Label and set the visibility to false to start. I placed the code to set the Label to visible after each of the if statements in the predicate logic so that it only shows if none of the statements are valid. I then placed a line setting it back to false at the beginning of the predicate logic so on each listener change it gets reset.

I am having an issue where if my search returns the last item in the list it is fine and the label doesn't appear, but if the search doesn't return the last item in the list then the label becomes visible even though the search displays items from the sorted list.

The following is my code and I've attached screenshots of the "bug".

FilteredList<Part> filteredParts = new FilteredList<>(Inventory.getAllParts(), p -> true);       

partFilter.textProperty().addListener((ob, ol, newValue) -> {
    filteredParts.setPredicate(text -> {

        //each time listener is updated sets label visibility to false
        partSearchMismatch.setVisible(false);

        //If searchbar is empty or null it displays all parts
        if (newValue == null || newValue.isEmpty()){
            return true;
        }
        //if text matches partID
        String lowerCaseFilter = newValue.toLowerCase();
        if(String.valueOf(text.getId()).contains(lowerCaseFilter)){

            return true;
        }
        //if text matches part name
        if(text.getName().toLowerCase().contains(lowerCaseFilter)){
            return true;
        }


        partSearchMismatch.setVisible(true); //if no matches, displays label 

        return false; //no match

    });

});
//Wraps filtered list in sorted list
SortedList<Part> sortedParts = new SortedList<>(filteredParts);

//Binds sorted list comparator to the parts table comparator
sortedParts.comparatorProperty().bind(partsTable.comparatorProperty());

partsTable.setItems(sortedParts);

The following is the base sample data in table

image1

Image of search that matches first object in list but label showing anyways

image2

Image of search that matches second object in list but label showing anyways

image3

Image of label working as long as last object in table is included in search

image4

CodePudding user response:

my project guidelines require me to also create some form of output upon a search not matching any objects in the TableView.

That will happen by default, when the filtered table does not have any matches for the filter, the table will display a placeholder. The placeholder node is configurable, so you can set it display anything you want.

You don't need an additional label and logic to show that nothing would be displayed in the table unless you have an additional requirement for that.

Okay, so it's the placeholder text that I see in the table that says "no content in table"? I should just be able to set that to something that mentions the search didn't return anything. Thank you. I am still curious what I am doing wrong with the label though

The predicate is a boolean function, it should be executed without side effects. You set the predicate on the filtered list, but you don't know or have any control over when the filtered list will choose to execute that code. The predicate may be executed many times to filter each individual item in the list. If you put side effects in the predicate like setting label visibility, you don't really know what will happen unless the operations are idempotent (they aren't).

Instead, have the predicate evaluate the lambda input object to see if it should be filtered or not, only returning true or false and doing nothing else.

That you name the input parameter to the predicate as text indicates that you don't really understand it. The input parameter is a Part, an element of the observed list, it is not text, so it would make sense to name it part and not text. Also (and this is just style, not functional), to help make the code easier to read, use a descriptive name for the new text in the listener (e.g. filterText or newSearchText rather than newValue).

For a concrete example of searching a table using a filtered list and a text field, see, the Eden Coding tutorial:

As noted by Slaw in comments:

If the placeholder is not enough, I would try to use bindings.

Something like:

label.visibleProperty().bind(
    Bindings.isEmpty(filteredParts)
        .and(
             partFilter.textProperty().isNotEmpty()
        )
    )
);

Or you can move the logic for the label notification setting out of the predicate but keep it in the change listener for the search text field (this essentially does a similar job to the binding).

  • Related