Ways to avoid having to sleep()/delay() your Katalon Studio code - katalon-studio
I have these test cases, see WebUI.delay() and sleep() everywhere, but they break as soon as I remove those delays...
How can I avoid having to delay my test cases?
There are very few legitimate cases where you would want to delay() your test case, or put it to sleep()...
Main legit use case for this, is to poll an API until you get some expected result from it...or to do some performance testing (namely to simulate an impatient user).
It is a code smell, because it:
is literally stopping the execution of your test case for some hard-coded amount of time, which means:
a lot of wasted time, or
situations where "it works on my machine (but not yours)!"
is a brute force solution to something where either a built-in waitForXXX() method exist, or some simple custom keyword can be written to solve the problem
Yet there are so many people using those methods because there seems to be no other choice...
How can we prevent these?
Let's go look at some examples:
Examples of preventable uses of delay()/sleep()
Keep in mind, most if not all of these are based on what I've had to face in testing a Zoho app, and based on my complete overhauling of the Katalon project for that...
With that in mind, let's take a look at the first use case I encountered when being tasked with that project...
Elements that are supposed to be on the page (especially after page load)
Bad way
The laziest way is to just do this:
WebUI.navigateTo(homePage);
WebUI.delay(5);
WebUI.click(findTestObject("Home page/Dashboard item"));
Better way
This is better way to do it...
WebUI.navigateTo(homePage);
final TestObject loader = findTestObject("Home page/loader");
WebUI.waitForElementVisible(loader, 2, FailureHandling.OPTIONAL);
WebUI.waitForElementNotVisible(loader, 5);
final TestObject dashboardItem = findTestObject("Home page/Dashboard item");
WebUI.waitForElementVisible(dashboardItem, 2);
WebUI.click(dashboardItem);
Custom widgets
Auto-complete
In Zoho (namely CRM), an auto-complete has three major parts:
text field
loading icon
dropdown list
Bad way
A noob may handle an autocomplete like:
WebUI.sendKeys(findTestObject("Page/My Autocomplete/input field"), "some text");
WebUI.delay(3); // DON'T DO THIS, PLEASE!!
WebUI.click(findTestObject("Page/My Autocomplete/first dropdown option"));
Heaven forbid it be a more complicated use case, where the autocomplete be fetch-on-scroll, and the dropdown option you're looking for be on the nth page, for n > 1.
Then what will you do? Some scroll, then some hard-coded delay(), inside for loop?
Better way
This strategy-based way involves a lot more code, but is way more correct:
We're going to create Custom Keyword for it!
public final class GeneralWebUIUtils {
public static void HandleAutoComplete(TestObject textField, String input, TestObject loader, TestObject dropdownOption, BaseSelectionStrategy strategy = null) throws StepFailedException {
WebUI.click(textField)
WebUI.sendKeys(textField, input)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementNotVisible(loader, 3)
},
"Loader",
"disappear");
TimeLoggerUtil.LogAction({
return WebUI.waitForElementPresent(dropdownOption, 3, FailureHandling.STOP_ON_FAILURE);
},
"Dropdown option",
"become present")
BaseSelectionStrategy selectionStrategy = strategy;
if (strategy == null)
selectionStrategy = new BaseSelectionStrategy(input);
selectionStrategy.doSelect(dropdownOption);
}
public static boolean WaitForElementCondition(Closure<Boolean> onCheckCondition, Closure onContinue, TestObject to, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
final long startTime = System.currentTimeMillis()
boolean isConditionSatisfied = false;
while ((System.currentTimeMillis() < startTime + timeOut * 1000) && (!isConditionSatisfied)) {
isConditionSatisfied = WebUI.waitForElementPresent(to, 1, failureHandling) && onCheckCondition(to);
if (onContinue != null)
onContinue(isConditionSatisfied, to);
}
if ((!isConditionSatisfied) && (failureHandling.equals(FailureHandling.STOP_ON_FAILURE))) {
KeywordUtil.markFailedAndStop("Condition for TestObject '${to.getObjectId()}' not met after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
return isConditionSatisfied;
}
public static boolean WaitForElementHasText(TestObject to, String expectedText, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForElementCondition({ TestObject testObj ->
return WebUI.getText(testObj).contains(expectedText);
}, { boolean success, TestObject testObj ->
if (!success) {
WebUI.waitForElementNotPresent(testObj, 1, FailureHandling.OPTIONAL);
WebUI.waitForElementPresent(testObj, timeOut);
}
},
to,
timeOut,
failureHandling);
}
}
public class BaseSelectionStrategy {
protected final String input;
public BaseSelectionStrategy(String input) {
this.input = input;
}
public void doSelect(TestObject dropdownOption) {
this.waitForDropdownOption(dropdownOption);
WebUI.click(dropdownOption);
}
public boolean waitForDropdownOption(TestObject dropdownOption, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return GeneralWebUIUtils.WaitForElementHasText(dropdownOption, input, this.getWaitTime(), failureHandling);
}
public int getWaitTime() {
return 5;
}
}
// this is only here to allow time-logging of an action
public final class TimeLoggerUtil {
public static boolean LogAction(Closure<Boolean> onAction, String elementDesc, String expectationDesc) throws StepFailedException {
final long startTime = System.currentTimeMillis();
try {
if (onAction()) {
KeywordUtil.logInfo("${elementDesc} took ${(System.currentTimeMillis() - startTime) / 1000} seconds to ${expectationDesc}");
return true;
} else {
KeywordUtil.markWarning("${elementDesc} didn't ${expectationDesc} after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
} catch (StepFailedException ex) {
KeywordUtil.markFailedAndStop("${elementDesc} didn't ${expectationDesc} after ${(System.currentTimeMillis() - startTime) / 1000} seconds...\n${ex.getMessage()}");
throw ex;
}
return false;
}
}
Then, to use it, simply be like:
GeneralWebUIUtils.HandleAutoComplete(
findTestObject("Page/My Autocomplete/input field"),
"some text", // or whatever
findTestObject("Page/My Autocomplete/loader"),
findTestObject("Page/My Autocomplete/first dropdown option"),
);
I will admit, this is a lot more code, but is way more of a robust solution as it will not hang your test case up!
What, your autocomplete is fetch-on-scroll? No problem!
Simply create a BaseScrollableSelectionStrategy (and an ActionHandler):
#InheritConstructors
// feel free to extend this class as you see fit
public class BaseScrollableSelectionStrategy extends BaseSelectionStrategy {
#Override
public void doSelect(TestObject dropdownOption) {
final long startTime = System.currentTimeMillis();
ActionHandler.Handle({
this.waitForDropdownOption(dropdownOption);
}, { boolean success, _ ->
KeywordUtil.logInfo("${dropdownOption.getObjectId()} ${this.getActionStatus(success, startTime)}")
if (!success) {
final TestObject lastAvailableDropdownItem = new TestObject("Last available dropdown item")
.addProperty("xpath",
ConditionType.EQUALS,
"//lyte-drop-box[not(contains(concat(' ', #class, ' '), ' lyteDropdownHidden '))]//lyte-drop-item[last()]"); // TODO: change this to the XPath for the last dropdown option on your auto complete drop-down
TimeLoggerUtil.LogAction({
if (!WebUI.waitForElementPresent(lastAvailableDropdownItem, 2))
return false;
GeneralWebUIUtils.ScrollDropdownOptionIntoView(lastAvailableDropdownItem);
return true;
}, lastAvailableDropdownItem.getObjectId(),
"scroll into view");
}
}, 15)
GeneralWebUIUtils.ScrollDropdownOptionIntoView(dropdownOption);
WebUI.click(dropdownOption);
}
protected String getActionStatus(boolean success, long startTime) {
if (success)
return "took ${(System.currentTimeMillis() - startTime) / 1000} seconds to show up";
return "didn't show up after ${(System.currentTimeMillis() - startTime) / 1000} seconds";
}
#Override
public int getWaitTime() {
return 2;
}
}
public class ActionHandler {
public static void Handle(Closure onAction, Closure onDone, long timeOut) {
long startTime = System.currentTimeSeconds();
while (System.currentTimeSeconds() < startTime + timeOut) {
try {
onDone(true, onAction());
return;
} catch (Exception ex) {
onDone(false, ex);
}
}
}
}
add a keyword for scrolling the dropdown option into view:
public static void ScrollDropdownOptionIntoView(TestObject to) {
WebUI.executeJavaScript("arguments[0].scrollIntoView({block: 'center'})", [WebUiCommonHelper.findWebElement(to, 3)])
WebUI.waitForElementVisible(to, 2)
}
and then your use case should look like:
final String textFieldInput = "some text" // or whatever
GeneralWebUIUtils.HandleAutoComplete(
findTestObject("Page/My Autocomplete/input field"),
textFieldInput,
findTestObject("Page/My Autocomplete/loader"),
findTestObject("Page/My Autocomplete/first dropdown option"),
new BaseScrollableSelectionStrategy(textFieldInput), // or whatever your derived scrollable selection strategy is
);
New Table Row
This is yet another unjustified use case that I had to deal with, this one much earlier on in the project. It was something like this:
Bad way
WebUI.click(findTestObject("Page/Add Button"));
WebUI.delay(3);
WebUI.click(findTestObject("Page/Second Row Dropdown button"));
Better way
WebUI.click(findTestObject("Page/Add Button"));
final TestObject secondRowDropdownBtn = findTestObject("Page/Second Row Dropdown button");
WebUI.waitForElementPresent(secondRowDropdownBtn, 3);
WebUI.scrollToElement(secondRowDropdownBtn, 2);
WebUI.click(secondRowDropdownBtn);
...or better yet...
WebUI.click(findTestObject("Page/Add Button"));
final TestObject secondRowDropdown = findTestObject("Page/Second Row Dropdown button");
WebUI.waitForElementPresent(secondRowDropdown, 3);
WebUI.scrollToElement(secondRowDropdown, 2);
WebUI.click(findTestObject("Page/Second Row Dropdown button"));
Saving a model to be created, and then doing/checking something on the next page
This is a common one. You are about to click "Submit", to create some record (item/rate card/discount/member/...)...
You wait for the button to disable...
...then you try that WebUI.waitForPageLoad(15); between waiting for the button to disappear, and waiting for the next page to fully load...
Test case fails! WHY.png
Bad way
You pull your hair out in frustration, and then give into that temptation to instead do WebUI.delay(15)...
It works, but it's slow?!
You now have to run that test case to create 10+ items/rate cards/discounts/members/... per a data file...
AnyMinuteNow.gif
Better way
This way handles both the save button and the URL change. It will require more custom keywords to our GeneralWebUIUtils:
public final class GeneralWebUIUtils {
private static boolean WaitForURLCondition(Closure<Boolean> onCheckCondition, int timeOut, Closure<String> onErrorMessage, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
final long startTime = System.currentTimeMillis()
boolean isConditionSatisfied = false;
while ((System.currentTimeMillis() < startTime + timeOut * 1000) && (!isConditionSatisfied)) {
isConditionSatisfied = onCheckCondition(WebUI.getUrl())
}
if ((!isConditionSatisfied) && (failureHandling.equals(FailureHandling.STOP_ON_FAILURE))) {
KeywordUtil.markFailedAndStop("${onErrorMessage(WebUI.getUrl())} after ${(System.currentTimeMillis() - startTime) / 1000} seconds");
}
return isConditionSatisfied;
}
public static boolean WaitForURLNotEquals(String url, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForURLCondition({ String browserURL ->
return !(browserURL =~ SMDStringUtils.GetURLPattern(url)).matches();
},
timeOut, { String browserURL ->
"URL '${browserURL}' matches unexpected '${url}'"
},
failureHandling)
}
public static boolean WaitForURLEquals(String url, int timeOut, FailureHandling failureHandling = FailureHandling.STOP_ON_FAILURE) {
return this.WaitForURLCondition({ String browserURL ->
return (browserURL =~ SMDStringUtils.GetURLPattern(url)).matches();
},
timeOut, { String browserURL ->
"URL '${browserURL}' does not match expected '${url}'"
},
failureHandling)
}
public static void HandleSaveButton(TestObject saveButton) throws StepFailedException {
this.HandleSaveButton(saveButton, true);
}
public static void HandleSaveButton(TestObject saveButton, boolean shouldSuccessfullySave) throws StepFailedException {
WebUI.scrollToElement(saveButton, 3)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementClickable(saveButton, 3, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"become clickable");
WebUI.click(saveButton)
TimeLoggerUtil.LogAction({
return WebUI.waitForElementHasAttribute(saveButton, GeneralWebUIUtils.DISABLED, 5, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"disable");
if (shouldSuccessfullySave) {
TimeLoggerUtil.LogAction({
return WebUI.waitForElementNotPresent(saveButton, 5, FailureHandling.STOP_ON_FAILURE);
},
"Save button",
"disappear from the DOM");
TimeLoggerUtil.LogAction({
WebUI.waitForPageLoad(5);
return true;
},
"Page",
"load")
}
}
}
public final class SMDStringUtils {
public static String GetURLPattern(String url) {
return (/^(http(s)?\:\/\/)?${url}(\/)?$/);
}
}
In the test case, we just do:
// submit the changes
GeneralWebUIUtils.HandleSaveButton(findTestObject('Page/Submit button'))
// verify that the save happened successfully
GeneralWebUIUtils.WaitForURLNotEquals(creationPageURL, 15) // creationPageURL is the URL of the creation page you were just on
or better yet, if you had to click some "Add" button on some initial page, and submit button is supposed to end up sending you back to that initial page:
// submit the changes
GeneralWebUIUtils.HandleSaveButton(findTestObject('Page/Submit button'))
// verify that the save happened successfully
GeneralWebUIUtils.WaitForURLEquals(initPageURL, 15) // initPageURL is the URL of the initial page you were just on
Thoughts? Concerns?
Let me know in the comments section below!
Related
How do I update a gtk listbox from an async method?
So when writing UI in GTK it's generally preferrable to handle reading of files, etc. in an Async Method. things such as listboxes, are generally bound to a ListModel, the items in the ListBox updated in accordance with the items_changed signal. So if I have some class, that implements ListModel, and has an add function, and some FileReader that holds a reference to said ListModel, and call add from an async function, how do i make that in essence triggering the items_changed and having GTK update accordingly? I've tried list.items_changed.connect(message("Items changed!")); but it never triggers. I saw this: How can one update GTK+ UI in Vala from a long operation without blocking the UI but in this example, it's just the button label that is changed, no signal is actually triggered. EDIT: (Codesample added at the request of #Michael Gratton //Disclaimer: everything here is still very much a work in progress, and will, as soon as I'm confident that what I have is not total crap, be released under some GPL or other open license. //Note: for the sake of readability, I adopted the C# naming convention for interfaces, namely, putting a capital 'I' in front of them, a decision i do not feel quite as confident in as I did earlier. //Note: the calls to message(..) was put in here to help debugging public class AsyncFileContext : Object{ private int64 offset; private bool start_read; private bool read_to_end; private Factories.IVCardFactory factory; private File file; private FileMonitor monitor; private Gee.Set<IVCard> vcard_buffer; private IObservableSet<IVCard> _vCards; public IObservableSet<IVCard> vCards { owned get{ return this._vCards; } } construct{ //We want to start fileops at the beginning of the file this.offset = (int64)0; this.start_read = true; this.read_to_end = false; this.vcard_buffer = new Gee.HashSet<IVCard>(); this.factory = new Factories.GenericVCardFactory(); } public void add_vcard(IVCard card){ //TODO: implement } public AsyncFileContext(IObservableSet<IVCard> vcards, string path){ this._vCards = vcards; this._vCards = IObservableSet.wrap_set<IVCard>(new Gee.HashSet<IVCard>()); this.file = File.new_for_path(path); this.monitor = file.monitor_file(FileMonitorFlags.NONE, null); message("1"); //TODO: add connect this.monitor.changed.connect((file, otherfile, event) => { if(event != FileMonitorEvent.DELETED){ bool changes_done = event == FileMonitorEvent.CHANGES_DONE_HINT; Idle.add(() => { read_file_async.begin(changes_done); return false; }); } }); message("2"); //We don't know that changes are done yet //TODO: Consider carefully how you want this to work when it is NOT called from an event Idle.add(() => { read_file_async.begin(false); return false; }); } //Changes done should only be true if the FileMonitorEvent that triggers the call was CHANGES_DONE_HINT private async void read_file_async(bool changes_done) throws IOError{ if(!this.start_read){ return; } this.start_read = false; var dis = new DataInputStream(yield file.read_async()); message("3"); //If we've been reading this file, and there's then a change, we assume we need to continue where we let off //TODO: assert that the offset isn't at the very end of the file, if so reset to 0 so we can reread the file if(offset > 0){ dis.seek(offset, SeekType.SET); } string line; int vcards_added = 0; while((line = yield dis.read_line_async()) != null){ message("position: %s".printf(dis.tell().to_string())); this.offset = dis.tell(); message("4"); message(line); //if the line is empty, we want to jump to next line, and ignore the input here entirely if(line.chomp().chug() == ""){ continue; } this.factory.add_line(line); if(factory.vcard_ready){ message("creating..."); this.vcard_buffer.add(factory.create()); vcards_added++; //If we've read-in and created an entire vcard, it's time to yield message("Yielding..."); Idle.add(() => { _vCards.add_all(vcard_buffer); vcard_buffer.remove_all(_vCards); return false; }); Idle.add(read_file_async.callback); yield; message("Resuming"); } } //IF we expect there will be no more writing, or if we expect that we read ALL the vcards, and did not add any, it's time to go back and read through the whole thing again. if(changes_done){ //|| vcards_added == 0){ this.offset = 0; } this.start_read = true; } } //The main idea in this class is to just bind the IObservableCollection's item_added, item_removed and cleared signals to the items_changed of the ListModel. IObservableCollection is a class I have implemented that merely wraps Gee.Collection, it is unittested, and works as intended public class VCardListModel : ListModel, Object{ private Gee.List<IVCard> vcard_list; private IObservableCollection<IVCard> vcard_collection; public VCardListModel(IObservableCollection<IVCard> vcard_collection){ this.vcard_collection = vcard_collection; this.vcard_list = new Gee.ArrayList<IVCard>.wrap(vcard_collection.to_array()); this.vcard_collection.item_added.connect((vcard) => { vcard_list.add(vcard); int pos = vcard_list.index_of(vcard); items_changed(pos, 0, 1); }); this.vcard_collection.item_removed.connect((vcard) => { int pos = vcard_list.index_of(vcard); vcard_list.remove(vcard); items_changed(pos, 1, 0); }); this.vcard_collection.cleared.connect(() => { items_changed(0, vcard_list.size, 0); vcard_list.clear(); }); } public Object? get_item(uint position){ if((vcard_list.size - 1) < position){ return null; } return this.vcard_list.get((int)position); } public Type get_item_type(){ return Type.from_name("VikingvCardIVCard"); } public uint get_n_items(){ return (uint)this.vcard_list.size; } public Object? get_object(uint position){ return this.get_item((int)position); } } //The IObservableCollection parsed to this classes constructor, is the one from the AsyncFileContext public class ContactList : Gtk.ListBox{ private ListModel list_model; public ContactList(IObservableCollection<IVCard> ivcards){ this.list_model = new VCardListModel(ivcards); bind_model(this.list_model, create_row_func); list_model.items_changed.connect(() => { message("Items Changed!"); base.show_all(); }); } private Gtk.Widget create_row_func(Object item){ return new ContactRow((IVCard)item); } }
Heres the way i 'solved' it. I'm not particularly proud of this solution, but there are a couple of awful things about the Gtk ListBox, one of them being (and this might really be more of a ListModel issue) if the ListBox is bound to a ListModel, the ListBox will NOT be sortable by using the sort method, and to me at least, that is a dealbreaker. I've solved it by making a class which is basically a List wrapper, which has an 'added' signal and a 'remove' signal. Upon adding an element to the list, the added signal is then wired, so it will create a new Row object and add it to the list box. That way, data is control in a manner Similar to ListModel binding. I can not make it work without calling the ShowAll method though. private IObservableCollection<IVCard> _ivcards; public IObservableCollection<IVCard> ivcards { get{ return _ivcards; } set{ this._ivcards = value; foreach(var card in this._ivcards){ base.prepend(new ContactRow(card)); } this._ivcards.item_added.connect((item) => { base.add(new ContactRow(item)); base.show_all(); }); base.show_all(); } } Even though this is by no means the best code I've come up with, it works very well.
Wicket 7 - AutoCompleted Text field - to have onSelect method
We would like to implement AutoCompleteTextField field, once user has selected the field from AutoComplete result, then system would auto populate on other text field, i have used the component AjaxFormComponentUpdatingBehavior (blur), however this will take effect on every text input from AutoCompleteTextField field, but if i change to AjaxFormComponentUpdatingBehavior (change), it doesnt work. Below is the sample code: AutoCompleteTextField<String> field_postcode = new AutoCompleteTextField<String>("field_postcode", new PropertyModel<String>(getModelObject(), "wAdditionalInfo.postal"), autoCompleteRenderer) { private static final long serialVersionUID = 1L; #Override protected Iterator<String> getChoices(String input) { if (Strings.isEmpty(input)) { List<String> emptyList = Collections.emptyList(); return emptyList.iterator(); } List<String> choices = new ArrayList<String>(); List<Postcode> postcodeList = getProfileManager().findAllPostcodeByPostcode(input); for (Postcode p : postcodeList) { String postcode = p.getPostcode(); if (postcode.startsWith(input)) { choices.add(p.getPostcode()); if (choices.size() == 10) { break; } } } return choices.iterator(); } }; field_postcode.setRequired(true); field_postcode.add(new AjaxFormComponentUpdatingBehavior("blur"){ private static final long serialVersionUID=-1107858522700306810L; #Override protected void onUpdate( AjaxRequestTarget target){ Postcode postcode = getProfileManager().findPostcodeByPostcode(field_postcode.getInput()); if (postcode != null) { City city = postcode.getCity(); State state = city.getState(); field_city.setModelObject(city.getCity()); ddl_state.setModelObject(state); if (isDisplayTip) { //isDisplayTip true mean is from widrawal webform isReadonly = true; } else { field_city.setEnabled(false); } ddl_state.setEnabled(false); } else { if (isDisplayTip) { isReadonly = false; } else { field_city.setEnabled(true); } ddl_state.setEnabled(true); } target.add(field_city, ddl_state); } } ); Is there any api from wicket to achieve this? We need to have something when user select the option from Auto complete, then it only onUpdate method of AjaxFormComponentUpdatingBehavior
According to https://github.com/apache/wicket/blob/cbc237159c4c6632b4f7db893c28ab39d1b40ed4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js#L620 it should trigger change event on the HTMLInputElement and thus notify you on the server side. Use the browser debugger to see whether https://github.com/apache/wicket/blob/cbc237159c4c6632b4f7db893c28ab39d1b40ed4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js#L453 is executed and whether it leads to an Ajax call with the value in the parameters.
GWT ValueChangeHandler and getting before value
I want to get values of my textBox before change its value and after changed its value. String beforeValue = ""; TextBox textBox = new TextBox(); textBox.addFocusHandler(new FocusHandler() { public void onFocus(final FocusEvent event) { beforeValue = textBox.getText(); } }); textBox.addValueChangeHandler(new ValueChangeHandler<String>() { public void onValueChange(final ValueChangeEvent<String> event) { System.out.println("Before value is " + beforeValue); System.out.println("After value is " + textBox.getText()); } }); As above codes , I need two handlers (FocusHandler and ValueChangeHadler) to get before value and after value . My question is how can I get it by one Handler or another simple and easy way ? I don't want to use two handlers to get it. Any suggestions would be appreciated. Thanks in advance !
Your idea(using 2 handlers) is fair enough but its buggy. I don't think it can be done in a better way. If you want to use a single handler, create a custom class wrapper using the two handlers. Here is the code for you. public abstract class MyValueChangeHandler<T> implements ValueChangeHandler<T> { T prevValue = null; T value = null; public MyValueChangeHandler(final ValueBoxBase<T> widget) { widget.addFocusHandler(new FocusHandler() { public void onFocus(FocusEvent event) { prevValue = widget.getValue(); } }); } #Override public void onValueChange(ValueChangeEvent<T> event) { value = event.getValue(); onValueChange(value, prevValue); // or // onValueChange(event, prevValue); prevValue = value; } public abstract void onValueChange(T value, T prevValue); // or // public abstract void onValueChange(ValueChangeEvent<T> event, T prevValue); } And you can use it as, TextBox box = new TextBox(); box.addValueChangeHandler(new MyValueChangeHandler<String>(box) { #Override public void onValueChange(String value, String prevValue) { Window.alert("Prev Value : " + prevValue + " CurrnetValue: " + value); } });
Netbeans lookup not getting executed first time when the application is started
I have an application in which lookup provider is in explorer window and it is a jcomboBox. Its selected value is provided as lookup to other top components and displayed one in editor and one in output window area. The value of the jcombobox is saved at the time of closing the application and initialized into the jcombobox when the application is started. When I clean and build the application and start the application, the initial value displayed in the jcombobox is not updated in the lookup and listner windows display uninitialized values. Once the new value is selected in the jcombobox it gets displayed in the other windows. If I close the application without changing the value of the jcombobox and start the application, the lookup is not updated. If I change the selection in the combobox, close the application and start again, the lookup gets updated. I expect anytime when the application is started, the other windows will get the value of the lookup. Any help in this regards will be appreciated. Thanks in advance. The code for the explorer window which provides the lookup is as follows: public final class ProviderTopComponent extends TopComponent { public ProviderTopComponent() { initComponents(); setName(Bundle.CTL_ProviderTopComponent()); setToolTipText(Bundle.HINT_ProviderTopComponent()); associateLookup(new AbstractLookup(content)); } private void initComponents() { jComboBox1 = new javax.swing.JComboBox(); jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); jComboBox1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { jComboBox1ActionPerformed(evt); } }); private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) { str = (String) jComboBox1.getSelectedItem(); content.set(Collections.singleton(str), null); } private final InstanceContent content = new InstanceContent(); String str; // Variables declaration - do not modify private javax.swing.JComboBox jComboBox1; void writeProperties(java.util.Properties p) { // better to version settings since initial version as advocated at // http://wiki.apidesign.org/wiki/PropertyFiles p.setProperty("version", "1.0"); p.setProperty("val", (String) jComboBox1.getSelectedItem()); } void readProperties(java.util.Properties p) { String version = p.getProperty("version"); // TODO read your settings according to their version String v = p.getProperty("val"); if(v != null) { jComboBox1.setSelectedItem(v); } } } The code for the lookup listener window is as follows: public final class Listner_1TopComponent extends TopComponent implements LookupListener{ public Listner_1TopComponent() { initComponents(); setName(Bundle.CTL_editorTopComponent()); setToolTipText(Bundle.HINT_editorTopComponent()); } ... private void initComponents() { ... jLabel1 = new javax.swing.JLabel(); ... Collection<? extends String> str_collection; private Lookup.Result<String> result = null; String str; // Variables declaration - do not modify private javax.swing.JLabel jLabel1; // End of variables declaration #Override public void componentOpened() { result = org.openide.util.Utilities.actionsGlobalContext().lookupResult(String.class); result.addLookupListener(this); } #Override public void componentClosed() { result.removeLookupListener(this); } #Override public void resultChanged(LookupEvent le) { str_collection = result.allInstances(); if (str_collection.isEmpty()) { } else { str = str_collection.iterator().next(); } jLabel1.setText(str); jLabel1.repaint(); } }
How to apply like search on GWT cell table?
I am using GWT 2.3.I which I am using GWT cell table. Here below is the code for my cell table: public class FormGrid extends SuperGrid { List<Form> formList; #Override public void setColumns(CellTable table) { TextColumn<Form> nameColumn = new TextColumn<Form>() { #Override public String getValue(Form object) { return object.getName(); } }; table.addColumn(nameColumn, "Name"); } #Override public void setData() { if (formList != null && formList.size() > 0) { AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() { #Override protected void onRangeChanged(HasData<Form> display) { int start = display.getVisibleRange().getStart(); int end = start + display.getVisibleRange().getLength(); end = end >= formList.size() ? formList.size() : end; List<Form> sub = formList.subList(start, end); updateRowData(start, sub); } }; provider.addDataDisplay(getTable()); provider.updateRowCount(formList.size(), true); } } public List<Form> getFormList() { return formList; } public void setFormList(List<Form> formList) { this.formList = formList; } } In this my set column and set data will be called fro super class flow.This cell table is working fine. Now I want to put a filter type facility (like search) in this cell table.It should be like, there is a texbox above the cell table and what ever written in that text box, it should fire a like query to all form name for that text box value. for example I have 1000 form in the grid.Now if user writes 'app' in some filter textbox above the cell table the all the form which have 'app' in there name will be filtered and grid has only those forms only. This is the first case: Another case is I am only render one column in grid name.I have two more properties in form (description,tag).But I am not rendering them.now for filter if user writes 'app' in filter box then it should make a query to all three (name, description, and tag) and should return if 'app' matched to any of three. I am not getting how to apply filter in cell table. Please help me out.Thanks in advance.
You can find an implementation in the expenses sample. Here is a short summary of the steps 1.) Create a Textbox and a SearchButton. 2.) add a clickHandler to the SearchButton (You can also add KeyUpHandler to the Textbox alternatively) searchButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { search(); } }); 3.) In the search function retrieve the searchString and store it. private void search() { searchString = searchBox.getText(); setData(); } 4.) modify your setdata() function to take searchString into account #Override public void setData() { if (formList != null && formList.size() > 0) { AsyncDataProvider<Form> provider = new AsyncDataProvider<Form>() { #Override protected void onRangeChanged(HasData<Form> display) { int start = display.getVisibleRange().getStart(); int end = start + display.getVisibleRange().getLength(); //new function if searchString is specified take into account List<Form> sub = getSubList(start,end); end = end >= sub.size() ? sub.size() : end; updateRowData(sub.subList(start, end);, sub); } }; provider.addDataDisplay(getTable()); provider.updateRowCount(formList.size(), true); } } private List<Form> getSubList(int start, int end) { List<Form> filtered_list = null; if (searchString != null) { filtered_list= new ArrayList<Form>(); for (Form form : formList) { if (form.getName().equals(searchString) || form.getTag().equals(searchString) || form.getDescription().equals(searchString)) filtered_list.add(form); } } else filtered_list = formList; return filtered_list; }
can propose another solution what can be used quite easy multiple times. Idea is to create custom provider for your celltable. GWT celltable filtering Video in this post shows it in action. Here is the part of code of custom list data provider which u have to implement. #Override protected void updateRowData(HasData display, int start, List values) { if (!hasFilter() || filter == null) { // we don't need to filter, so call base class super.updateRowData(display, start, values); } else { int end = start + values.size(); Range range = display.getVisibleRange(); int curStart = range.getStart(); int curLength = range.getLength(); int curEnd = curStart + curLength; if (start == curStart || (curStart < end && curEnd > start)) { int realStart = curStart < start ? start : curStart; int realEnd = curEnd > end ? end : curEnd; int realLength = realEnd - realStart; List<t> resulted = new ArrayList<t>(realLength); for (int i = realStart - start; i < realStart - start + realLength; i++) { if (filter.isValid((T) values.get(i), getFilter())) { resulted.add((T) values.get(i)); } } display.setRowData(realStart, resulted); display.setRowCount(resulted.size()); } } }