I have a list of items, above which there is an input fields.
The input field is a filter, it should filter the list based on the text you key in to the input field.
For example :
If you type "th", it should filter the list so that all the items should start with "th".
For this I am using AjaxFormComponentUpadingBehavior("onkeypress").
But this does not seem to be working they way it should.
When I key in something it clears up that and takes the cursor to the first letter of the input field.
I have tried onkeyup and onkeydown, and all of them act the same way.
For now I am doing the filter on a link click which works, but I want it to be as seamless as onkeypress.
Is there a way to achieve this?
I am using wicket 1.4.x
Here is the code :
// Customer Filter input field
customerFilterTxt = new TextField<String>("customerFilterTxt", new PropertyModel<String>(this, "slectedCustomerFilterStr"));
customerFilterTxt.setOutputMarkupPlaceholderTag(true);
customerListViewContainer.add(customerFilterTxt);
// Ajax behavior for customer group filter auto complete input filed
AjaxFormComponentUpdatingBehavior customerGroupFilterBehave = new AjaxFormComponentUpdatingBehavior("onkeypress") {
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
List<CustomerGroupBean> filterList = new ArrayList<CustomerGroupBean>();
if(Util.hasValue(selectedCustomerGroupFilterStr)) {
String str = selectedCustomerGroupFilterStr.toUpperCase();
for(CustomerGroupBean group : custGroupList) {
if(group.getRightGroupName().toUpperCase().contains(str)) {
filterList.add(group);
}
}
custGroupListView.setList(filterList);
} else {
custGroupListView.setList(custGroupList);
}
target.addComponent(customerFilterTxt);
target.addComponent(custGroupListViewContainer);
}
};
customerGroupFilterTxt.add(customerGroupFilterBehave);
You're adding the input field to the update call within the update method. This instructs Wicket to replace the input field, rendering the text field again. Thats why the cursor jumps to the first position. Why do you add the text field to the update? I don't see any imperative for it. Also you may want to use the event "onkeyup".
Related
I found this
In which the accepted answer is almost perfect, but I would like to instead add the elements to an ArrayList. Is this possible?
I'll try to be concise: I'm creating a JavaFX application and I have an ArrayList containing all my TextFields. I used this list to add an event handler for the Action event to each field, and this handler calls a method that moves focus to the next field in the list (this way the user can press return and will navigate to the next field automatically).
In the same loop I also add an event listener to each field, so that if a field loses focus another method is called to update the form. In this way the user can navigate however they choose (return, tab, mouse click, etc.), and the form will update automatically without the need for a button click when they are finished entering data.
Now, in the update method, I determine which field triggered the event and validate the text in the field and parse a double from it. I then need to do something with that data. Here is where I want to use my ArrayList of "worker functions." I can simply create a parallel ArrayList that matches the index order of the field traversal ArrayList, and call the correct update method.
I want to do it this way because each TextField is tied to a specific member of a specific object for example - the TextField qtyOrdered would be tied to the workOrder.qtyOrdered member, and the TextField materialWidth would be tied to the masterRoll.materialWidth member. These objects would of course have their own accessors and mutators, so I wanted to use the interface to create all these methods beforehand and call the correct one based on the index of the TextField.
I want something like this (pseudocode):
ArrayList<Worker> workerMethods = new ArrayList<>();
workerMethods.add(new Worker() {
public void work(double input) {
workOrder.setQtyOrdered(input);
}
});
//Add remaining methods...
method updateForm(TextField caller)
{
get callerIndex;
// validate user input
if (valid)
workerMethods.get(callerIndex).work(input);
}
In fact, using the method I linked to, this works with arrays like so:
workerMethods[callerIndex].update(callerValue);
However, when I use the same syntax for adding to an ArrayList instead it obviously does not work.
I would like to use an ArrayList instead of an array if possible. This is because some fields in the form are removed from the traversableFields ArrayList when they are deactivated so that field traversal will skip these fields when they are hidden from view, and resume in the correct order when they are visible. For example, if the user wants to estimate the length of material on the master roll, they can click a CheckBox to show those fields.
The Action event handler for that CheckBox would then call my updateControls() method which would enable the required fields, set them to visible, and add them to the list of traversable fields. If they uncheck it, the reverse happens.
If I am using an array to store my update methods, the indexes may not match the indexes of my traversableFields List at any given time.
Edit: Added my actual code below (this is a simplified version I am using in a small test environment).
Code for creating the list of traversable TextFields:
// ArrayList of traversable TextFields for form navigation.
private ArrayList<TextField> traversableFields = new ArrayList<>();
// Add all TextFields to the ArrayList.
traversableFields.addAll(Arrays.asList(field1, field2, field3));
// Loop through the ArrayList to add handlers/listeners.
for (int i = 0; i < traversableFields.size(); i++) {
// Get the Control at index i in the ArrayList.
TextField currentControl = traversableFields.get(i);
// Add an event handler so that when the user presses return we can
// move to the next field in the ArrayList.
currentControl.addEventHandler(ActionEvent.ACTION, e ->
moveToNextField(e));
// Add a listener so that if a field loses focus we update the form.
currentControl.focusedProperty().
addListener((obs, wasFocused, isNowFocused) ->
{
if (!isNowFocused) {
updateForm(currentControl);
}
});
}
Code for creating the array of worker methods:
interface Update { public void update(double input); }
class Label1 { public void update(double input) { label1.setText(String.valueOf(input)); } }
class Label2 { public void update(double input) { label2.setText(String.valueOf(input)); } }
class Label3 { public void update(double input) { label3.setText(String.valueOf(input)); } }
Label1 l1 = new Label1();
Label2 l2 = new Label2();
Label3 l3 = new Label3();
Update[] updateFunctions = new Update[] {
new Update() { public void update(double input) { l1.update(input); } },
new Update() { public void update(double input) { l2.update(input); } },
new Update() { public void update(double input) { l3.update(input); } }
};
The snippet above is basically exactly what they did in the link I posted. As I mentioned earlier, this works. I can call updateFunctions[callerIndex].update(callerValue); and it does what I want. Here is the method called by the event handler:
// Move to the next field in the form. Called by the Action Event for each
// field in the traversableFields ArrayList.
private void moveToNextField(ActionEvent event)
{
// Get the TextField that triggered the event.
TextField caller = (TextField)event.getSource();
// Get the index of this Control from the ArrayList.
int callerIndex = traversableFields.indexOf(caller);
// If we have reached the end of the list then we move to the
// first field in the list.
if (callerIndex == traversableFields.size() - 1)
traversableFields.get(0).requestFocus();
// Otherwise move to the next field.
else
traversableFields.get(++callerIndex).requestFocus();
}
and here is the method called by the focus listener:
// Update the form. This will contain the majority of functional code
// and will be called automatically when any TextField loses focus.
private void updateForm(TextField caller)
{
// Get the index of the TextField
int callerIndex = traversableFields.indexOf(caller);
// Get the CSS id of the TextField for testing purposes.
String callerID = caller.getId();
// Get the TextField contents.
String callerText = caller.getText();
// Flag variable.
boolean fieldEmpty = callerText.equals("");
// Double for storing parsed input.
double callerValue = 0;
// If the field is not empty, ensure that it contains valid data before
// proceeding.
if (!fieldEmpty)
{
try
{
callerValue = Validation.getDouble(callerText);
updateFunctions[callerIndex].update(callerValue);
clearError(caller);
}
catch (Exception e)
{
// If the input was invalid, alert the user and move focus
// back to the offending TextField.
System.out.println("Invalid input.");
markEntryInvalid(caller);
caller.requestFocus();
}
}
// Trace statements.
System.out.println("updateForm() called by " + callerID);
System.out.println("Parsed value was " + callerValue);
}
However, I want to use an ArrayList instead of an array. But if I do this:
ArrayList<Update> updateMethods = new ArrayList<>();
updateMethods.add(new Update() { public void update(double input) { l1.update(input); } });
I get <identifier> expected error.
If I understand your question correctly, what you want to know is how to initialize an ArrayList with a given collection of values?
That would be done like this:
List<Workers> myWorkers = new ArrayList<>(
Arrays.asList(
new Worker() { ... },
new Worker() { ... }
)
);
Arrays.asList(element1, element2, ...) returns an immutable list of these elements. And the constructor of ArrayList can take a Collection to initialize the list.
Of course, you can also create the list and add single elements. The resulting list is the same:
List<Workers> myWorkers = new ArrayList<>();
myWorkers.add(new Worker() { ... });
myWorkers.add(new Worker() { ... });
In both cases, the resulting list can be modified using add and remove methods.
Two notes on the usecase, though:
First, a Map might be better suited for your usecase, because you don't need to take care about indexes:
// initialization:
Map<TextField, Worker> workers = new HashMap<TextField, Worker>();
workers.put(textfield1, worker1);
workers.put(textfield2, worker2);
// ...
void updateForm(TextField caller) {
workers.get(caller).work();
}
Second, if your Worker interface only has one method, since Java 8 you can use it as a functional interface with a closure. So the initialization would look like this:
Map<TextField, Worker> workers = new HashMap<TextField, Worker>();
workers.put(textfield1, input -> { /* do something with input here */ });
workers.put(textfield2, input -> { /* do something with input here */ });
And maybe your Worker interface is even not needed and you can just use Consumer<InputType> instead.
You can create List<Runnable> and execute the code in the current thread or in the parallel thread:
List<Runnable> workerMethods = List.of(
() -> System.out.println("worker 1"),
() -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get(0).run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get(1)).start(); // worker 2
Similarly, you can create Map<String, Runnable>:
Map<String, Runnable> workerMethods = Map.of(
"w1", () -> System.out.println("worker 1"),
"w2", () -> System.out.println("worker 2"));
// execute code in the current thread
workerMethods.get("w1").run(); // worker 1
// execute code in a new thread
new Thread(workerMethods.get("w2")).start(); // worker 2
Might I suggest an entirely different approach?
Why not use the user data field of the TextField to point to your own object that can handle all of your needs.
e.g.
class FieldContext {
TextField previous;
TextField next;
Update updater;
// add whatever else you need, e.g. validation interface etc.
}
so when you get an event from the TextField you can just call:
FieldContext fieldCtx = (FieldContext)((TextField)event.getSource()).getUserData();
and then you are free to handle whatever specific event processing is needed having the context information for the TexTField.
I find a lot of people overlook the fact that you have a field for user data on the controls. I find it can simplify things quite a bit
I have this particular problem now - I have a grid that I am trying to have the data filtered through multiple filters. For that, I am using textboxes that serve as input fields for my filtering criterion.
My grid has three columns (First Name, Last Name, Address) and I would like to be able to chain the filtering operations one after the other. All of the values are taken from a MySQL database.
Essentially the filter process should go like this:
FirstName ^ LastName ^ Address
For example, grid with three columns:
And in the filter for First Name column, I input the variables Aa, which would result in the table looking like this:
However, if I decided input D into the Last Name filter it returns results like this (ignores the modifications by the first filter):
Instead of the expected result which would look like this:
The way I am filtering through the grid is like this:
firstNameFilter.addValueChangeListener( e->
{
Notification.show(e.getValue());
ldp.setFilter(desc ->
{
return StringUtils.containsIgnoreCase(desc.getFName(), firstNameFilter.getValue());
});
});
firstNameFilter.setValueChangeMode(ValueChangeMode.EAGER);
What would be the best way to filter through multiple columns whilst taking into consideration previous filter actions?
listDataProvider.setFilter(...) will overwrite any existing filter.
I have written an answer about this very topic, with a complete example code ready for copy paste, and screenshots showing that the multiple filters work as expected.
The most important takeaway from it is this:
Every time that any filter value changes, I reset the current filter using setFilter. But within that new Filter, I will check the values of ALL filter fields, and not only the value of the field whose value just changed. In other words, I always have only one single filter active, but that filter accounts for all defined filter-values.
Here is how it could look with your code:
firstNameFilter.addValueChangeListener( e-> this.onFilterChange());
lastNameFilter.addValueChangeListener( e-> this.onFilterChange());
addressFilter.addValueChangeListener( e-> this.onFilterChange());
// sidenote: all filter fields need ValueChangeMode.EAGER to work this way
private void onFilterChange(){
ldp.setFilter(desc -> {
boolean fNameMatch = true;
boolean lNameMatch = true;
boolean addressMatch = true;
if(!firstNameFilter.isEmpty()){
fNameMatch = StringUtils.containsIgnoreCase(desc.getFName(), firstNameFilter.getValue());
}
if(!lastNameFilter.isEmpty()){
lNameMatch = StringUtils.containsIgnoreCase(desc.getLName(), lastNameFilter.getValue());
}
if(!addressFilter.isEmpty()){
addressMatch = StringUtils.containsIgnoreCase(desc.getAddress(), addressFilter.getValue());
}
return fNameMatch && lNameMatch && addressMatch;
});
});
I'm new to JavaFX and was wondering if the Bindings API allowed an easier way to achieve the following. Consider a model that contains a database that may be null (because the database loads asynchronously) and a view that displays a label status reflecting the state of the database. If it is null it should say something like "Loading..." and if it isn't it should display how many items are in the database. It also would be great if the status could reflect the size of the database as it grows or shrinks.
So far, I understand that I could bind an integer property (size of the database) to the text property of the label by using a converter. This is fine, but I want the label to display more than the number. A localized string like "Loaded {0} items" precisely. And let's not forget that the database may still be null.
This is the solution I have in place
#Override
public void initialize(URL url, ResourceBundle bundle) {
// Initialize label with default value
status();
model.databaseProperty().addListener((obs, old, neu) -> {
// Update label when database is no longer null
status();
// Update label when size of database changes
neu.sizeProperty().addListener(x -> status());
});
}
public void status() {
if (model.database() == null) {
status.setText(bundle.getString("status.loading"));
} else {
String text = bundle.getString("status.ready");
int size = model.database().size();
text = new MessageFormat(text).format(size);
status.setText(text);
}
}
It works, but is there a way to do it with a chain of bindings, or at least part of it? I've seen how powerful (and lenghty) boolean bindings can be but I'm not sure something as flexible is possible with string bindings.
You can use Bindings.when, which is essentially a dynamic if/then binding:*
status.textProperty().bind(
Bindings.when(model.databaseProperty().isNull())
.then(bundle.getString("status.loading"))
.otherwise(
Bindings.selectInteger(model.databaseProperty(), "size").asString(
bundle.getString("status.ready")))
);
However, the above assumes bundle.getString("status.ready") returns a java.util.Formatter string, not a MessageFormat string. In other words, it would need to be "Loaded %,d items" rather than "Loaded {0,number,integer} items".
Bindings doesn’t have built-in support for MessageFormat, but if you really want to stick with MessageFormat (which is a legitimate requirement, as there are things MessageFormat can do which Formatter cannot), you can create a custom binding with Bindings.createStringBinding:
MessageFormat statusFormat = new MessageFormat(bundle.getString("status.ready"));
status.textProperty().bind(
Bindings.when(model.databaseProperty().isNull())
.then(bundle.getString("status.loading"))
.otherwise(
Bindings.createStringBinding(
() -> statusFormat.format(new Object[] { model.getDatabase().getSize() }),
model.databaseProperty(),
Bindings.selectInteger(model.databaseProperty(), "size")))
);
* Actually, it’s more like the ternary ?…: operator.
I am currently working on a tool which edits data dynamically in a JTable. I want to hide the targeted row whenever a button is clicked. Right now I am using RowFilter. Whenever the button isClicked, a new filter is created:
RowFilter<MyTableModel, Object> rowFilter = null;
try {
rowFilter = RowFilter.notFilter(RowFilter.regexFilter(((String)dataTable.getValueAt(dataTable.getSelectedRow(), 0)),0));
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rowFilter);
This only works for one element each time the button is clicked. I want to stay them hidden, so you can continously hide elemtens in the table. It is important to mention that I do not want to delete the rows, just hide them.
I hope someone has an easy answer for this, looking for quite a while now.
This method sorter.setRowFilter(rowFilter); is replacing the filter every time you "add" a new filter. So, it's "forgetting" the old rules. What you have to do is edit the existing filter to include the new rules for filtering.
Check out the documentation for more details.
In any case, I extracted a part of the documentation which you should try to implement.
From RowFilter Javadoc:
Subclasses must override the include method to indicate whether the
entry should be shown in the view. The Entry argument can be used to
obtain the values in each of the columns in that entry. The following
example shows an include method that allows only entries containing
one or more values starting with the string "a":
RowFilter<Object,Object> startsWithAFilter = new RowFilter<Object,Object>() {
public boolean include(Entry<? extends Object, ? extends Object> entry) {
for (int i = entry.getValueCount() - 1; i >= 0; i--) {
if (entry.getStringValue(i).startsWith("a")) {
// The value starts with "a", include it
return true;
}
}
// None of the columns start with "a"; return false so that this
// entry is not shown
return false;
}
};
This means that the include() method is going to return true or false depending if an item should be shown.
Therefore, you should only set the RowFilter once, and reimplment the include() method to match all the rules you currently have set upon your view.
I have two columns which are orderbyborder links. When i click one column i changed the color of column by adding attributeModifier in the following way
add(new AttributeModifier("style", true, new Model<String>("background-color:#80b6ed;")));
This works fine. But when i click on second column, the first column remains the changed color. But I expect only the column which i click should hold this attributeModifier!
You shouldn't change the modifier.
The trick is to have your model return the correct value. So instead of using new Model<String>("background-color:#80b6ed;"), which always returns the same constant value, you'd have something like:
new Model<String>() {
#Override
public String getObject() {
if( columnName.equals( selectedColumn ) { //or something along these lines, to check if the current column is the selected one
return "background-color:#80b6ed;";
}
return "background-color:white;";
}
}
And of course this also means you can add an attribute modifier to every column when you create them and don't have to worry about them later on.
Another way to achieve what you want is to add a css class to the selected line via Javascript (removing the class from old one).