Vaadin checkbox is not correctly rendered - vaadin-flow

Using Vaadin 14.0.13 without compatibility mode.
I use a view to create a Dialog with dynamic content:
#Route("")
public class MainView extends VerticalLayout {
public MainView(DialogContentProvider contentProvider) {
this.add(new Button("Click me!", event -> new Dialog(contentProvider.create()).open()));
}
}
The contentProvider is an interface
public interface DialogContentProvider {
Component create();
}
with this implementation:
public class CheckBoxContentProvider implements DialogContentProvider {
#Override
public Component create() {
return new Checkbox("My checkbox", true);
}
}
instantiated by Spring Boot (version 2.2.1.RELEASE) with a bean:
#Bean
public DialogContentProvider contentProvier() {
return new CheckBoxContentProvider();
}
When I click on the button, the dialog is opened but the checkbox haven't the box:
The source code is on github: https://github.com/gronono/bug-vaadin-checkbox
I don't understand why and how I can fix it. If I include the checkbox creation inside the main view, it works fine:
#Route("")
public class MainView extends VerticalLayout {
public MainView(DialogContentProvider contentProvider) {
// this.add(new Button("Click me!", event -> new Dialog(contentProvider.create()).open()));
this.add(new Button("Click me!", event -> new Dialog(new Checkbox("My checkbox", true)).open()));
}
}

This sound an awful lot like this (related github issue)
Basically, this happens when you don't have any View that uses a Checkbox directly, but through other means like reflection or in your case the contentProvider, because in no view of your app there is any import statement of Checkbox (--> therefore, vaadins scan during the installation will not detect usages of Checkbox, so it will not download npm stuff for checkbox).
in the github it says this will be fixed in 14.1
If you need a fix now, for me it worked when I declared a field of that type in any view with a #Route. That field doesn't have to be used.
#Route("")
public class MainView extends VerticalLayout {
private Checkbox unusedCheckbox; // this line fixes it.
public MainView(DialogContentProvider contentProvider) {
this.add(new Button("Click me!", event -> new Dialog(contentProvider.create()).open()));
}
}
Addendum: This is not related to the Checkbox component specifically, it happens with any vaadin component that isn't initially scanned in a route, but used anyway through reflective-, provider-, or generic means.
Edit: You can also work around this currently by adding a #Route(registerAtStartup = false) to your provider that uses the Checkbox directly. This will make vaadins scan see the checkbox usage (therefore importing its npm package), but will not actually register the provider as a real route..
Another way which I prefer if you need this for multiple components is to create a new View with a #Route(registerAtStartup = false) which only defines private variables for each component that you'll need in the application (and arent already used directly in some view of yours). This has the advantage of all these component usage definitions in one place, and once the official fix is released, you need only to delete one class and the deprecated workaround is gone.
#Route(registerAtStartup = false)
public class ComponentImportView extends VerticalLayout {
private Checkbox checkBox;
private Upload upload;
private ProgressBar progressBar;
}

Related

Vaadin Tabs based component created using Designer doesn't show data when bound using its Java companion file

Below is the Vaadin Designer code for simple tab functionality
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import '#vaadin/vaadin-tabs/src/vaadin-tabs.js';
import '#vaadin/vaadin-tabs/src/vaadin-tab.js';
class TestUi extends PolymerElement {
static get template() {
return html`
<style include="shared-styles">
:host {
display: block;
height: 100%;
}
</style>
<vaadin-tabs theme="equal-width-tabs" id="vaadinTabs">
<vaadin-tab id="vaadinTab">
Product Overview
</vaadin-tab>
<vaadin-tab id="vaadinTab1">
Product DetailView
</vaadin-tab>
<vaadin-tab id="vaadinTab2">
Reports
</vaadin-tab>
</vaadin-tabs>
`;
}
static get is() {
return 'test-ui';
}
static get properties() {
return {
// Declare your properties here.
};
}
}
customElements.define(TestUi.is, TestUi);
It's corresponding Java companion file looks as below
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.templatemodel.TemplateModel;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
/**
* A Designer generated component for the test-ui template.
*
* Designer will add and remove fields with #Id mappings but
* does not overwrite or otherwise change this file.
*/
#Tag("test-ui")
#JsModule("./src/productdetailview/test-ui.js")
public class TestUi extends PolymerTemplate<TestUi.TestUiModel> {
#Id("vaadinTabs")
private Tabs vaadinTabs;
#Id("vaadinTab")
private Tab vaadinTab;
#Id("vaadinTab1")
private Tab vaadinTab1;
#Id("vaadinTab2")
private Tab vaadinTab2;
/**
* Creates a new TestUi.
*/
public TestUi() {
// You can initialise any data required for the connected UI components here.
vaadinTabs.addSelectedChangeListener(selectedChangeEvent -> {
selectedChangeEvent.getSelectedTab().getElement().getStyle().set("background-color":"blue");
});
}
/**
* This model binds properties between TestUi and test-ui
*/
public interface TestUiModel extends TemplateModel {
// Add setters and getters for template properties here.
}
}
In the above code, My thinking was to start writing the selectedChangeListener Handler directly without doing much but instead this doesn't work and below initialization code needs to be added.
//I have added for one tab but it requires all the tabs to be added
vaadinTabs = new Tabs();
vaadinTab = new Tab();
vaadinTabs.add(vaadinTab);
My question here is why would I need to initialize when the Polymer js code generated using Vaadin Designer clearly defines the tab and it's group?
This is the same issue with Vaadin Grid. Even after defining the columns in the Polymer js, I have to redefine it from the Java component end instead of directly start providing the data via data provider
TLDR; Unfortunately, you have encountered this issue IllegalArgumentException when switching tabs
which is closed as won't fix.
My question here is why would I need to initialize when the Polymer js code generated using Vaadin Designer clearly defines the tab and it's group?
Generally, you don't need to. But Tabs doesn't work as intended in this case. Thus, for this particular component, it's suggested to not mix template/Java logic.
For example, you can verify it with a <vaadin-text-field>, where event is fired correctly.
Java counterpart
#Id("vaadinTextField")
private TextField vaadinTextField;
/**
* Creates a new TestUi.
*/
public TestUi() {
// You can initialise any data required for the connected UI components here.
vaadinTextField.addValueChangeListener(event->{
System.out.println("Event has happened");
});
vaadinTextField.setValueChangeMode(ValueChangeMode.EAGER);
and snippet for the template right after the tabs:
<vaadin-vertical-layout id="vaadinVerticalLayout" style="width: 100%; height: 100%;">
<vaadin-text-field id="vaadinTextField"></vaadin-text-field>
</vaadin-vertical-layout>
Taken from the issue:
So all Tab related API methods in Tabs are completely broken in regard to injected Tabs.
and
Unfortunately we've concluded that there is no sensible way we can support this for now, thus this issue will be a known limitation with Tabs. It will not work as #Id mapped component when the child vaadin-tabs are created in the template file, so you should not try to mix client & server logic and content for the Tabs component.
As a workaround, you could try to use your own component for #Id mapping tabs like:
#Tag("vaadin-tabs")
public IdMappedTabs extends Component {
public IdMappedTabs() {
}
public Registration addSelectionListener(PropertyChangeListener listener) {
return getElement().addPropertyChangeListener("selected", listener);
}
public void setSelectedTabIndex(int index) {
getElement().setProperty("selected", index);
}
}
Edit:
What is the issue with Grid you are having? (There is a good tutorial about Designer, where Grid is used. It might be useful : Vaadin Designer tutorial)

Xamarin.iOS: Create a custom button with an additional object

In my Xamarin.iOS application there are many buttons with the same scheme but different from to standart UIButton. I created one class for the buttons because most of the features are same but for example the textcolor or the backgroundcolor are different.
So how can I put an extra information about any button in the storyboard?
And how can I react on it in code?
You can make your custom element visible in for the designer with the DesignTimeVisible and the Register Attribute like
[Register ("CustomButton"), DesignTimeVisible (true)]
public class CustomButton: UIButton {
[Export ("CustomProperty "), Browsable (true)]
public int CustomProperty {get; set;}
public CustomButton(IntPtr handle) : base (handle) { }
public CustomButton()
{
// Called when created from code.
Initialize ();
}
public override void AwakeFromNib ()
{
// Called when loaded from xib or storyboard.
Initialize ();
}
void Initialize ()
{
// Common initialization code here.
CustomProperty = 0xB00B5;
}
}
For all properties that you want to set in the Designer you just hae to add Export and Browsable (true). In the Initializeyou can set all the vlaues of the common properties.
It will appear in the Toolbox under Custom Components. You might have to rebuild.
And the Custom Property can be modified in the Properties pane
More info: https://developer.xamarin.com/guides/ios/user_interface/designer/ios_designable_controls_overview/

Forcing a Vaadin Tabsheet to use the CloseHandler

I have a Vaadin Tabsheet. All tabs are closable. I have defined a custom CloseHandler. When a Tab is closed via the small x button, the the CloseHadler executes; however, if I close the tab programmatically
TabSheet parent = (TabSheet) this.getParent();
parent.removeTab(parent.getTab(this));
The close handler does not execute. Is there a way to force the CloseHandler to execute before the Tab is removed.
Thanks,
Oliver
A solution would be to extend the TabSheet class and override removeTab() to force it to execute the closeHandler. As the TabSheet.closeHandler is private you'll need to override this field and its setter too. Vaadin could make things simpler (changing the closeHandler to protected or providing a getter) but I don't see it as a "dirty" solution.
public class MyTabSheet extends TabSheet {
private static final long serialVersionUID = 1L;
private CloseHandler closeHandler;
#Override
public void removeTab(Tab tab) {
if (closeHandler != null) {
closeHandler.onTabClose(this, tab.getComponent());
}
super.removeTab(tab);
}
#Override
public void setCloseHandler(CloseHandler handler) {
closeHandler = handler;
// needed for TabSheet.TabsheetServerRpcImpl
super.setCloseHandler(handler);
}
}
If you want you could create a feature request at Vaadin (vaadin.com/bug), maybe the closeHandler should be called by default. There's already the #10555 but it's 3 years old...

VaadinSession attribute and updating session-bound components

I have a Vaadin Navigator with multiple View elements. Each view has a different purpose however some also contain common traits that I have put inside custom components.
One of those custom components is the menu - it is positioned at the top and allows navigation between the different views. I create and add this component inside the constructor of each view (if you are interested in the menu's implementation see the end of this post). Here is a skeleton for each custom view:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
}
}
So far, so good. In order to make things simple I will explain my problem using a simple label and not one of my other custom components but the dependency that I will describe here is the same for those components just like with the label.
Let's say I have a label which sole purpose is to display a greeting with the user's username. In order to do that I use VaadinSession where I store the attribute. This is done by my LoginController, which validates the user by looking into a database and if the user is present, the attribute is set and one of the views is opened automatically. The problem is that VaadinSession.getCurrent().getAttribute("username") returns null when called inside the constructor. This of course makes sense omho because a constructor should not be bound by a session-attribute.
So far I have managed to use the enter() method where there is no problem in retrieving session attributes:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
Label greeting = new Label("Hello " + username);
addComponent(greeting);
}
}
The issue that comes from this is obvious - whenever I open the view where this label is present, a new label is added so if I re-visit the view 10 times, I will get 10 labels. Even if I move the label to be a class member variable the addComponent(...) is the one that screws things up. Some of my custom components really depend on the username attribute (in order to display user-specific content) hence I also have to place those in the enter(...) method. The addComponent(...) makes a mess out of it. I even tried the dirty way of removing a component and then re-adding it alas! in vain:
class MyViewX implements View {
MenuViewComponent mvc;
Label greeting;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
#Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
greeting = new Label("Hello " + username);
// Remove if present
try { removeComponent(greeting); }
catch(Exception ex) { }
// Add again but with new content
addComponent(greeting);
}
}
but it's still not working. So my question is: what is the simplest way of updating a component that requires session-bound attributes?
The navigation via the menu custom component is omho not the issue here since all components of the menu are loaded in it's constructor. That's why it's also load that component in particular in a view's own constructor. Here is an example of a button in my menu that opens a view:
#SuppressWarnings("serial")
#PreserveOnRefresh
public class MenuViewComponent extends CustomComponent {
public MenuViewComponent(boolean adminMode) {
HorizontalLayout layout = new HorizontalLayout();
Label title = new Label("<h2><b>Vaadin Research Project</b></h2>");
title.setContentMode(ContentMode.HTML);
layout.addComponent(title);
layout.setComponentAlignment(title, Alignment.TOP_LEFT);
Button personalDashboardButton = new Button("Personal dashboard", new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
getUI().getNavigator().navigateTo(MainController.PERSONALDASHBOARDVIEW);
}
});
personalDashboardButton.setStyleName(BaseTheme.BUTTON_LINK);
layout.addComponent(personalDashboardButton);
layout.setComponentAlignment(personalDashboardButton, Alignment.TOP_CENTER);
// Add other buttons for other views
layout.setSizeUndefined();
layout.setSpacing(true);
setSizeUndefined();
setCompositionRoot(layout);
}
}
PERSONALDASHBOARDVIEW is just one of the many views I have.
It may be worth considering how long should your view instances "live", just as long they're displayed, until the session ends or a mix of the two. With this in mind and depending on what needs to happen when you enter/re-enter a view, you have at least the following 3 options:
1) Recreate the whole view (allowing for early view garbage-collection)
first register a ClassBasedViewProvider (instead of a StaticViewProvider) which does not hold references to the created views:
navigator = new Navigator(this, viewDisplay);
navigator.addProvider(new Navigator.ClassBasedViewProvider(MyView.NAME, MyView.class));
simple view implementation
public class MyView extends VerticalLayout implements View {
public static final String NAME = "myViewName";
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// initialize tables, charts and all the other cool stuff
addComponent(new SweetComponentWithLotsOfStuff());
}
}
2) Keep some already created components and replace others
public class MyView extends VerticalLayout implements View {
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here or things that don't change on enter
addComponent(new MyNavigationBar());
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff. great, let's do some cleanup first
removeComponent(mySweetComponentWithLotsOfStuff);
// initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
// show it
addComponent(mySweetComponentWithLotsOfStuff);
}
}
3) Lazy creating and updating (or not) the content when entering
public class MyView extends VerticalLayout implements View {
private boolean isFirstDisplay = true;
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here, as the user may not even see this view
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff
if (isFirstDisplay) {
isFirstDisplay = false;
// lazily initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
addComponent(mySweetComponentWithLotsOfStuff);
} else {
// maybe trigger component updates, or simply don't do anything
mySweetComponentWithLotsOfStuff.updateWhateverIsRequired();
}
}
}
I'm sure (and curious) that there may be other options, but I've mainly used a variation of 1) using spring with prototype views and component tabs.

ViewModel data lost when adding tab in TabHost

I am am trying to create a tabbed view using the Cirrious Conference sample but seem to be having problems displaying my data. The problem arises when the view which hosts the tab is invoked specifically at the code below
// Initialize a TabSpec for each tab and add it to the TabHost
spec = TabHost.NewTabSpec("welcome");
spec.SetIndicator(this.GetText("Welcome"),Resources.GetDrawable(Resource.Drawable.Tab_Welcome));
spec.SetContent(CreateIntentFor(ViewModel.Welcome));
TabHost.AddTab(spec);
My ViewModel calss is below
public class WelcomeViewModel : MvxViewModel
{
private string _description;
public string Description
{
get
{
return _description;
}
set
{
_description = value;
FirePropertyChanged("Description");
}
}
public WelcomeViewModel(string description)
{
Description = description;
}
public WelcomeViewModel()
{
}
}
The ViewModel.Welcome object is created and initialised with the data passed to it and remains so until control passes the SetContent method but when AddTab is called the WelcomeViewModel constructor seems to be invoked again and the data is reset. The call stack traces this to the
base.OnCreate(bundle);
call in the BaseTabbedView class. How do I keep hold of the data in the WelcomeViewModel so that it is displayed in my tab, Many Thanks
I have a feeling this is probably a bug I fixed a couple of weeks ago.
The bug was that the cache used for storing these child viewmodels was starting its indexing at 0 - and 0 was also being treated as the empty value by the Android Intent.
In vnext, see this checkin https://github.com/slodge/MvvmCross/commit/bcebadffee3850857d6a73070704e72b3aa72bcb
In master, see https://github.com/slodge/MvvmCross/commit/c3171e27168bf2b7e48dd73d105f5a08870cec5a

Resources