Remove a generated panel in GWT after a button is clicked - java

I am attempting to create a Google Web Toolkit (GWT) application that also uses Google Gears, but every time I try to remove the panel, I get an exception and the panel stays there.
Here is an excerpt from the exception I get (I've only included the relevant bits of the call stack, the rest just descends into the included function below):
java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list
at com.google.gwt.user.client.ui.RootPanel.detachOnWindowClose(RootPanel.java:122)
at com.google.gwt.user.client.ui.RootPanel.get(RootPanel.java:197)
I'm not sure what the problem is, but I really don't like leaving the button there after they approve the use of Gears.
What am I doing wrong? Or any suggestions on a different way I could do this to make it work?
if(!gearsFactory.hasPermission()) {
HorizontalPanel rightPanel = new HorizontalPanel();
rightPanel.getElement().setId("gearsPrompt");
rightPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
rightPanel.setSpacing(0);
rightPanel.setHeight("28px");
InlineLabel enableGearsText = new InlineLabel("Enable Gears for off-line access");
enableGearsText.getElement().setId("gearsText");
enableGearsText.addStyleName("titleElement");
rightPanel.add(enableGearsText);
final Button gearsButton = new Button("Use Gears");
gearsButton.getElement().setId("gearsButton");
gearsButton.addStyleName("titleElement");
gearsButton.setHeight("24px");
gearsButton.addClickHandler( new ClickHandler() {
public void onClick(ClickEvent event) {
Factory gearsFactory = Factory.getInstance();
if(gearsFactory != null) {
if(gearsFactory.getPermission()) {
RootPanel gearsPrompt = RootPanel.get("gearsPrompt");
gearsPrompt.removeFromParent();
}
}
}
});
rightPanel.add(gearsButton);
RootPanel titleBarRight = RootPanel.get("titleBarRight");
titleBarRight.add(rightPanel);
}

One solution I've found is to loop through all of the widgets under the "titleBarRight" panel and remove all widgets it contains:
if(gearsFactory.getPermission()) {
RootPanel titleBarRight = RootPanel.get("titleBarRight");
java.util.Iterator<Widget> itr = titleBarRight.iterator();
while(itr.hasNext()) {
itr.next();
itr.remove();
}
}
But somehow this still seems hacky and not quite the "right way to do it."

I know this is old, but how about...
gearsButton.addClickHandler( new ClickHandler() {
public void onClick(ClickEvent event) {
Factory gearsFactory = Factory.getInstance();
if(gearsFactory != null) {
if(gearsFactory.getPermission()) {
Button btn=(Button) event.getSource();
btn.removeFromParent();
}
}
}
});

Is there any reason for using RootPanel.get("gearsPrompt").removeFromParent(); instead of your own rightPanel.removeFromParent();? The reference is already there.

You can do :
theParentWidget.remove(index);
and the first child corresponds to 0;

Related

How to clear the field after i set ComboBox value as null?

I have a ComboBox called projectRequirementComboBox that is dependent from projectComboBox, from where I get the list to show in the dropdown in projectRequirementComboBox, but I want to do something like: when a user changes the project I want to empty projectRequirementComboBox, to be more clear none of the item will not be selected I am doing this right now but still my projectRequirementComboBox has the old value, I don't know what am I missing .I am using vaadin.version 8.0.7 .
private void refreshProjectRequirementCombobox()
{
List<ProjectRequirement> projectRequirements = new ArrayList<>();
if (projectComboBox.getValue() != null)
{
projectRequirements = projectRequirementService.findCurrentProjectRequirements(projectComboBox.getValue().getProjectId());
}
projectRequirementComboBox.setItems(projectRequirements);
projectRequirementComboBox.setValue(null);
}
private void loadProjectRequirement(Project project)
{
List<ProjectRequirement> projectRequirements = new ArrayList<>();
if (project != null)
{
projectRequirements = projectRequirementService.findCurrentProjectRequirements(project.getProjectId());
}
projectRequirementComboBox.setItems(projectRequirements);
}
I call refreshProjectRequirementCombobox here.
projectComboBox.addValueChangeListener(event ->
{
refreshProjectRequirementCombobox();
loadRejectReason();
});
Normally this should work. I created a minimum example with two ComboBoxes "main" and "dependent". The selection of the dependent ComboBox depends on the selection of the main ComboBox. Therefore there is a ValueChangeListener on the main ComboBox that resets the items and the selected value of the dependent ComboBox. When you start the application you see that the offered items of the dependent ComboBox change and that none of these new items is selected.
I think you have to post more of your code (where do you call refreshProjectRequirementCombobox from?) to see what you are doing different.
Here is my example minimum project code:
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final ComboBox<String> main = new ComboBox<>();
final ComboBox<String> dependent = new ComboBox<>();
final Map<String, String[]> dependentsByMain = new HashMap<>();
dependentsByMain.put("A", new String[]{"AA", "AB", "AC"});
dependentsByMain.put("B", new String[]{"BA", "BB", "BC"});
dependentsByMain.put("C", new String[]{"CA", "CB", "CC"});
List<String> mainItems = new ArrayList<>(dependentsByMain.keySet());
main.setItems(mainItems);
dependent.setItems(Arrays.asList("Test1", "Test2", "Test3"));
dependent.setValue("Test1");
main.addValueChangeListener((HasValue.ValueChangeListener<String>) valueChangeEvent -> {
if (valueChangeEvent.getValue() != null) {
dependent.setItems(dependentsByMain.get(valueChangeEvent.getValue()));
dependent.setValue(null);
}
});
layout.addComponents(main, dependent);
setContent(layout);
}
UPDATE:
Have a look at Srinivasan Sekar's answer and its comments. This is a bug in the used version (8.0.7) which seems to be fixed in version 8.5 (according to https://github.com/vaadin/framework/issues/9047#issuecomment-437864866). I tried my example code with version 8.7.1 so it works. With version 8.0.7 it doesn't.
So the main solution is to update the used Vaadin version. As a workaround (when unable to upgrade the Vaadin version) you FIRST have to set the ComboBox's value to null and THEN set the new items. So in my example the ValueChangeListener must look like:
main.addValueChangeListener((HasValue.ValueChangeListener<String>) valueChangeEvent -> {
if (valueChangeEvent.getValue() != null) {
dependent.setValue(null);
dependent.setItems(dependentsByMain.get(valueChangeEvent.getValue()));
}
});
There is open issue in Vaadin https://github.com/vaadin/framework/issues/9566 which refers to https://github.com/vaadin/framework/issues/2813
Going through the issue I found that by creating a custom combobox you can fix the issue:
public class ClearableComboBox<T> extends ComboBox<T> {
public ClearableComboBox(String in) {
super(in);
}
protected void setSelectedFromServer(T item) {
String key = itemToKey(item);
T oldSelection = getSelectedItem().orElse(getEmptyValue());
doSetSelectedKey(key);
fireEvent(new SingleSelectionEvent<>(ClearableComboBox.this, oldSelection, false));
}
}
Additionally, make sure to call setValue before calling setItems to clear items.
cmb.setValue(null);
cmb.setItems(aEmptyCollection);

Vaadin GridLayout not updating correctly

I have a problem where Vaadin does not update the display of a GridLayout in time. The GridLayout is a component in a VerticalLayout, that I use to list all uploaded files. When I upload a file everything works fine on the server-side but the client does not see the change until he creates a new request to the server (by uploading another file or triggering some other event / rarely the update works fine though).
Here is the component that contains the problematic GridLayout:
public class ListedMultiFileUpload extends VerticalLayout {
private MultiFileUpload multiFileUpload;
private GridLayout fileList;
public ListedMultiFileUpload(UploadFinishedHandler uploadFinishedHandler, UploadStateWindow uploadStateWindow) {
multiFileUpload = new MultiFileUpload(uploadFinishedHandler, uploadStateWindow);
fileList = new GridLayout(2, 5);
fileList.setImmediate(true);
addComponents(multiFileUpload, fileList);
}
public SmartMultiUpload getSmartUpload() {
return multiFileUpload.getSmartUpload();
}
public void addFile(String fileName, final Runnable fileRemover) {
final Label label = new Label(fileName);
final Button button = new Button("X");
button.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
fileList.removeComponent(label);
fileList.removeComponent(button);
fileRemover.run();
}
});
fileList.addComponent(label);
fileList.addComponent(button);
markAsDirtyRecursive();
}
}
I already tried setting the GridLayout to immediate as well as marking the whole component as dirty but nothing seems to make a difference.
So basically am I doing something wrong here? Or if not, is there a "nice" way I could force the client to update its components?
As proposed by #Morfic I enabled the Server-Push addon in automatic mode and now the required updates to the client are made by that addon. Not sure how much I like that solution but it works.

How to set value on click of link in vaadin

I am new to vaadin. I have one Link like
Link link = new Link("", new ExternalResource(redirectURL));
my requirement is, I have to set value when user clicks the link. Can I add listener when user click the link. Or is there alternate ways of setting value if link is clicked.
To capture onClick on a link or a label, I always create a HorizontalLayout and put the component inside it:
HorizontalLayout hor = new HorizontalLayout();
final Link link = new Link("Click on Me!", new ExternalResource("http://www.google.com"));
hor.addComponent(link);
hor.addLayoutClickListener(new LayoutClickListener() {
#Override
public void layoutClick(LayoutClickEvent event) {
// capture the click here and do whatever you'd like to do, e.g.
// if ( event.getClickedComponent() != null ) {
// if(event.getClickedComponent().equals(link)) {}
}
});
I interpreted your question as changing the caption of the link. As far as I know it's not possibly with the Link component. Take a look at the activelink addon: http://vaadin.com/addon/activelink.
This addon behaves like Link and lets you add a LinkActivatedListener to it. The code should look like this:
final ActiveLink link = new ActiveLink("", new ExternalResource(redirectURL));
link.addListener(new LinkActivatedListener() {
#Override
public void linkActivated(LinkActivatedEvent event) {
link.setCaption("newCaption");
}
});
You could use the new BrowserWindowOpener class:
From the API:
Component extension that opens a browser popup window when the
extended component is clicked.
Example:
BrowserWindowOpener browserWindowOpener = new BrowserWindowOpener(new ExternalResource("http://google.com"));
/*
* Apparently, the BrowserWindowOpener method setWindowName uses the HTML5 target
* attribute (no longer deprecated as it was in HTML4).
* So you can use either a frame name, or one of four special attribute values:
* _blank, _self, _parent, _top
*
* browserWindowOpener.setWindowName();
*/
final Button btn = new Button("Click me");
browserWindowOpener.extend(btn);
btn.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
btn.setCaption("clicked");
}
});
More information here.
I dint work on Vaadin yet But I looked into the document. I found that the Link class internally extends AbstractComponent class which has many functions which you can override. like it has addListener function where you need to pass the Component listener as a parameter and can detect the click event and do whatever you want to.
For reference check this
and this too
Hope this will help :)

GWT Form upload without refresh or forward

I'm wondering if what should be done for GWT com.google.gwt.user.client.ui.FileUpload
to upload to server without refresh or forwarding.
I have implemented the Form upload from this Java Doc, however it forwards the browser page to the target server URL.
How can I implement a form upload without forwarding?
Update:
final FormPanel form = new FormPanel();
form.setAction("/upload");
form.setEncoding(FormPanel.ENCODING_MULTIPART);
form.setMethod(FormPanel.METHOD_POST);
VerticalPanel panel = new VerticalPanel();
form.setWidget(panel);
final TextBox tb = new TextBox();
tb.setName("textBoxFormElement");
panel.add(tb);
// Create a FileUpload widget.
FileUpload upload = new FileUpload();
upload.setName("uploadFormElement");
panel.add(upload);
DOM.getElementById("form_panel").appendChild(panel.getElement());
Button submit = new Button("Submit");
panel.add(submit);
DOM.sinkEvents(submit.getElement(), Event.ONCLICK);
DOM.setEventListener(submit.getElement(), new EventListener(){
#Override
public void onBrowserEvent(Event event) {
if (event.getTypeInt() == Event.ONCLICK) {
form.submit();
return;
}
}});
form.addSubmitHandler(new FormPanel.SubmitHandler() {
public void onSubmit(SubmitEvent event) {
if (tb.getText().length() == 0) {
Window.alert("The text box must not be empty");
event.cancel();
}
}
});
form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
public void onSubmitComplete(SubmitCompleteEvent event) {
Window.alert(event.getResults());
}
});
IMO you are making things more complex than they are.
I don't understand why, if you are using widgets, you are trying to manage the DOM by hand.
1.- Attach your form panel to the root panel using GWT way, otherwise you are going to break widget hierarchy.
RootPanel.get("form_panel").add(panel);
2.- Try not to use sink-events by hand, and use methods already present in widgets:
submit.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
form.submit();
}
});
FormPanel sets its target to a hidden <iframe> so you will never be redirected to another page.
BTW, I'd rather use gwtupload library which simplifies so much uploading code in GWT, and adds a lot of additional features.
I used before gwt-upload library.
You dont need to rediscover America.
Thanks for moxie group
gwt-upload-project page

GWTQuery live blur not working

The following code attaches a new element with every subsequent click on the 2nd div. With each adding, the focus is set to the added element, so that it is ready for blur methods (I am setting the tabIndex attribute just for this purpose).
However, on clicking outside the new element, nothing happens, even though there should be a pop-up. Can anyone tell me what's wrong with this code?
public void onModuleLoad() {
VerticalPanel vert = new VerticalPanel();
String foo = "<div id ='foo'>Foo</div>";
$("#bodywrapper").append(foo);
$("#bodywrapper").append("<div id ='boo'>Boo</div>");
$("#boo").click(new Function() {
public boolean f(Event e) {
// Window.alert("foo");
$("<div id ='goo' tabIndex = '1'>Boo</div>").appendTo("#bodywrapper").focus();
return true;
}
});
$("#goo").live("blur", new Function() {
public boolean f(Event e) {
Window.alert("Foo");
return true;
}
});
RootPanel.get().add(vert);
}
}
The blur and focus events don't work with event delegation (live or delegate methods) because they're not bubbling events. JQuery introduce the special events focusout and focusin for this purpose. But GwtQuery doesn't support them yet. Please open an issue there and they will be implemented

Categories