Vaadin 7.6.2
Take the following example:
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
public class MyClass extends Panel {
TextField myField = new TextField();
HorizontalLayout hLayout = new HorizontalLayout( myField );
VerticalLayout vLayout = new VerticalLayout( myField );
Button button = new Button( "Press Me" );
public MyClass() {
super();
applySettings();
}
private void applySettings() {
button.addClickListener(new ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
setContent( hLayout );
}
});
vLayout.addComponent( button );
this.setContent( vLayout );
}
}
When I click the button the vLayout disappears but the hLayout (with myField) doesn't appear. What step am I'm missing? Or, is there a different way to do this?
If I add a secondary text field, like so:
TextField myField = new TextField();
TextField myField2 = new TextField(); // tf2
HorizontalLayout hLayout = new HorizontalLayout( myField );
VerticalLayout vLayout = new VerticalLayout( myField2 ); // tf2
It appears to work, however what I'm trying to achieve is the ability to dynamically switch my layouts using the fields (and their data) from the switched-out layout.
One component can not have 2 parents at the same time (hLayout & vLayout in your case), thus if it already has one, Vaadin will remove it from the previous parent and add it as a child to the current one. This is the addComponent method inherited from AbstractComponentContainer:
/**
* This only implements the events and component parent calls. The extending
* classes must implement component list maintenance and call this method
* after component list maintenance.
*
* #see com.vaadin.ui.ComponentContainer#addComponent(Component)
*/
#Override
public void addComponent(Component c) {
// Make sure we're not adding the component inside it's own content
if (isOrHasAncestor(c)) {
throw new IllegalArgumentException(
"Component cannot be added inside it's own content");
}
if (c.getParent() != null) {
// If the component already has a parent, try to remove it
AbstractSingleComponentContainer.removeFromParent(c);
}
c.setParent(this);
fireComponentAttachEvent(c);
markAsDirty();
}
If you're in debug mode, you can somewhat see an image of the composition tree in your browser by adding ?debug to your URL, something like http://localhost:8080/?debug
Well, it looks like I may have solved it.
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
public class MyClass extends Panel {
TextField myField = new TextField();
HorizontalLayout hLayout = new HorizontalLayout();
VerticalLayout vLayout = new VerticalLayout();
Button button = new Button( "Press Me" );
public MyClass() {
super();
applySettings();
}
private void applySettings() {
button.addClickListener(new ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
vLayout.removeAllComponents(); // this is optional
hLayout.addComponent( myField );
setContent( hLayout );
}
});
vLayout.addComponents( myField, button );
this.setContent( vLayout );
}
}
If I don't add myField in the layout constructors, but add it later in the code, it seems to work.
UPDATE
After more testing it seems whichever layout LAST called addComponent() for a given field, THAT layout gets the handle to that field. This behavior seems odd and if someone could explain why this is? That would be enlightening.
Related
I have written below code, where I want to add HorizontalLayout with TextBox and Button in Vaadin Grid. But when I run the code it is showing the output as
com.vaadin.flow.component.orderedlayout.HorizontalLayout#145a03ea
com.vaadin.flow.component.orderedlayout.HorizontalLayout#1f7acb46
package com.packagename.myapp;
import java.util.ArrayList;
import java.util.List;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
#Route("table")
public class TestTable extends VerticalLayout {
private Grid<TblValue> grid = new Grid<>();
public TestTable() {
init();
}
private void init() {
grid.addColumn(TblValue::getLayout);
grid.setItems(createData());
add(grid);
}
private List<TblValue> createData(){
List<TblValue> data = new ArrayList<>();
data.add(new TblValue());
data.add(new TblValue());
return data;
}
private class TblValue {
HorizontalLayout layout = new HorizontalLayout();
private Text txt = new Text("test string");
private Button btn = new Button("Submit");
public TblValue() {
layout.add(txt, btn);
}
public HorizontalLayout getLayout() {
return layout;
}
public void setLayout(HorizontalLayout layout) {
this.layout = layout;
}
}
}
As mentioned in the comments by Tatu Lund, the solution to your problem is to use grid.addComponentColumn(...) instead of grid.addColumn(...).
The addColumn(...) method will always show a String. If the valueprovider returns any Object, it will show its string value with String.valueOf(object). This is what you are seeing. com.vaadin.flow.component.orderedlayout.HorizontalLayout#145a03ea is what you get when you call String.valueOf(myHorizontalLayout).
addComponentColumn(...) on the other hand will take the returned HorizontalLayout of the valueProvider, and put the actual component into the cell which is what you wanted.
I have a splitlayout in my vaadin application in which in the first column I should show different pages added through the addToPrimary method, while in the second column I should have a page which contains an IFrame with a videoconference. Now the problem is that when I change route in the first column, even the second is updated and this refreshes the IFrame. The implementation of the showRouterLayoyutContent is the following:
#Override
public void showRouterLayoutContent(HasElement content) {
if (this.accessControl.isAccessGranted(UI.getCurrent(), ((ContentView) content).getName()) && ((ContentView) content).getName().equals("contattaView") ) {
setLayoutCall((com.vaadin.flow.component.Component) content);
}
else if (this.accessControl.isAccessGranted(UI.getCurrent(), ((ContentView) content).getName())) {
setLayoutContent((com.vaadin.flow.component.Component) content);
}
}
And the two methods setLayoutCall and setLayoutContent are the following:
private void setLayoutContent(com.vaadin.flow.component.Component content) {
split.addToPrimary(content);
}
private void setLayoutCall(com.vaadin.flow.component.Component content) {
split.addToSecondary(content);
split.setThemeName("visible-split");
}
How can I avoid to refresh the entire content when I update the first column of the split layout through navigation?
UPDATE: I'm showing also a very simple code on which I'm testing. The following class is the main layout:
private SplitLayout split = new SplitLayout();
private HorizontalLayout hl = new HorizontalLayout();
private Div firstDiv = new Div();
private Div secondDiv = new Div();
public MainView() {
Button button = new Button("Click me",
event -> Notification.show("Clicked!"));
final VerticalLayout menuBar = new VerticalLayout();
menuBar.add(new RouterLink("first view", FirstView.class));
menuBar.add(new RouterLink("second view", SecondView.class));
menuBar.setAlignItems(Alignment.CENTER);
add(menuBar);
//split.addToPrimary(firstDiv);
//split.addToSecondary(secondDiv);
//firstDiv.setId("first");
//secondDiv.setId("second");
//hl.add(firstDiv,secondDiv);
add(split);
//add(hl);
}
#Override
public void showRouterLayoutContent(HasElement element) {
if(element!=null && element.getClass().getName().contains("FirstView")) {
split.addToPrimary((Component) element);
//firstDiv.removeAll();
//firstDiv.add((Component) element);
//firstDiv.removeAll();
//firstDiv.getElement().appendChild(new Element[]{element.getElement()});
}
else if(element!=null && element.getClass().getName().contains("SecondView") ) {
secondDiv.removeAll();
secondDiv.add((Component) element);
split.addToSecondary((Component) element);
//split.addToSecondary(element.getElement().getComponent().get());
}
}
While these are the two views added to the split:
#Route(value="v1",layout=MainView.class)
public class FirstView extends VerticalLayout implements RouterLayout {
public FirstView() {
add(new Label("First View"));
}
}
#Route(value = "v2",layout=MainView.class)
public class SecondView extends VerticalLayout implements RouterLayout {
public SecondView() {
IFrame frame = new IFrame();
frame.setSrc("https://www.youtube.com/watch?v=LoigVtPCYPk&list=RDLoigVtPCYPk&start_radio=1");
add(frame);
}
}
Your comment does indeed seem to be the issue.
I recommend creating a Div wrapper for the primary content, and instead changing the content of that.
private final Div wrapper;
public MyLayout() {
wrapper = new Div();
wrapper.setSizeFull();
split.addToPrimary(wrapper);
}
private void setLayoutContent(com.vaadin.flow.component.Component content) {
wrapper.removeAll();
wrapper.add(content);
}
You might also want to do the same for the secondary. In addition, to prevent any components from being automatically removed when navigating, you can override removeRouterLayoutContent as well (available in Vaadin 14)
#Override
public void removeRouterLayoutContent(HasElement oldContent) {
// Do nothing, we remove manually in showRouterLayoutContent
}
Edit
If you can't override removeRouterLayoutContent, you can try creating your own instance of the HasElement to add. This is a bit of a hack, but might be the simplest solution.
public void showRouterLayoutContent(HasElement content) {
if (content.getClass().getSimpleName().contains("TestView")) {
// Creating a new instance should stop it from being auto removed
content = new TestView();
firstDiv.add((Component) content);
}
...
}
Vaadin 7.6.2
When I mouseover a field that has failed validation I see the error message as expected, however the delay before this message appears is too slow and should be immediate, as the user can click into the field BEFORE the message pops open.
Is there a way to adjust the time it takes to display a validation message?
Add the following css to your custom theme and compile. The default animate in/out time is 120ms, below I have changed the animate in time to 1ms to get rid of the validators tooltip delay.
.v-contextmenu[class*="animate-in"] {
-webkit-animation: valo-overlay-animate-in 1ms;
-moz-animation: valo-overlay-animate-in 1ms;
animation: valo-overlay-animate-in 1ms;
}
.v-contextmenu[class*="animate-out"] {
-webkit-animation: valo-animate-out-fade 120ms;
-moz-animation: valo-animate-out-fade 120ms;
animation: valo-animate-out-fade 120ms;
}
UI Code...
#Theme("mytheme")
#Widgetset("com.kevin.vaadin.sample.MyAppWidgetset")
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final TextField name = new TextField();
name.setCaption("Type your name here:");
Button button = new Button("Click Me");
button.addClickListener( e -> {
layout.addComponent(new Label("Thanks " + name.getValue()
+ ", it works!"));
});
name.addValidator(new StringLengthValidator("Name is too short.", 5, 10000000, false));
layout.addComponents(name, button);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
#WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
I'm new of vaadin and now I'm running in the following trouble. I have a TabSheet that contains a few tabs.
Now my Tax Code component setted in immediate mode and others it has a the following ValueChangeLister
TextField taxCode = new TextField();
taxCode.setImmediate(true);
taxCode.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
if(taxCode.isModified()){
searchByTaxCode(taxCode.getValue());
}
}
});
then each time that the user changes the value to taxCode component, the method searchByTaxCode is invoked. This happens also when the user switch from Tab2 to Tab1 and I don't want this?
How can I fix this problem?
This simple test app does not fire the ValueChangeEvent when switching tabs, only when actually changing the value in the TextField. You have something more that could influence the behaviour?
public class TabSheetEventsUI extends UI {
#WebServlet(value = "/tabsheetevents/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = TabSheetEventsUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
TabSheet tabSheet = new TabSheet();
Panel tab1 = new Panel("Tab 1");
Panel tab2 = new Panel("Tab 2");
tabSheet.addTab(tab1);
tabSheet.addTab(tab2);
final TextField tab1tf = new TextField("tab1tf");
tab1tf.setImmediate(true);
tab1tf.addValueChangeListener(new ValueChangeListener() {
#Override
public void valueChange(ValueChangeEvent event) {
System.out.println("Value change event catched.");
}
});
tab1.setContent(tab1tf);
setContent(tabSheet);
}
}
Hi I am trying to create a global class for my Native Menu, but can seem to get it to load in my Screen Class, I want this to show up where ever I would like to show up.
Not sure if I am doing it right
Here is my MenuItems Class
public final class MenuItems extends MainScreen {
public void getMenuItems(){
MenuItem myItem = new MenuItem(new StringProvider("My Cards"), 0x230000, 0);
myItem.setCommandContext(new Object(){
public String toString(){
return "My Cards";
}
});
myItem.setCommand(new Command(new CommandHandler(){
public void execute(ReadOnlyCommandMetadata metadata, Object context){
// Do Something
}
}));
addMenuItem(myItem);
}
}
The Screen Class I want to add it to is this, not sure if how I would call it here, I tried creating a new instance and just fetching the get method, but no luck, but if I dump the code from the above class in to this class, it will work fine, but I don't want that.
public final class MobiScreen extends MainScreen {
ToolBar toolbar = new ToolBar();
Banner banner = new Banner("Welcome");
MenuItems myMenu = new MenuItems();
public MobiScreen()
{
setTitle(toolbar.getToolBar());
setBanner(banner.getBanner());
myMenu.getMenuItems();
}
}
Why not have MobiScreen extend your MenuItems class?
public class MobiScreen extends MenuItems { ... }