How would I navigate between sub-views in Vaadin UI. I want my header on the website to stay static like a master page and the content view to change using navigation. How can I do that in Vaadin.
Well you could simply design your UI with a header (and other stuff if needed) and a component that will act as a placeholder for the changing content.
Then I add a method that receives the new content to display and puts it inside the place holder.
Here is some code:
public class MyUI extends UI implements ErrorHandler {
// I usually use a layout as a place holder
private VerticalLayout content;
...
#Override
public void init(VaadinRequest request) {
...
content = new VerticalLayout();
content.setSizeFull();
final VerticalLayout layout = new VerticalLayout(header, menu, content, footer);
layout.setMargin(true);
setContent(layout);
}
public void changeContent(Component view) {
content.removeAllComponents();
content.addComponent(view);
content.setComponentAlignment(view, Alignment.MIDDLE_CENTER);
}
}
If in you application you need to support browser's bookmarks and navigation (backward and forward buttons) you'll need to use URIFragments, described on the book of vaadin.
A common way of using it is with a Navigator:
public class NavigatorUI extends UI {
Navigator navigator;
protected static final String MAINVIEW = "main";
#Override
protected void init(VaadinRequest request) {
getPage().setTitle("Navigation Example");
// Create a navigator to control the views
navigator = new Navigator(this, this);
// Create and register the views
navigator.addView("", new StartView());
navigator.addView(MAINVIEW, new MainView());
} }
Your views will need to implemente the View interface as described in the book of vaadin
And if you use Spring, you could also use an addon, like SpringVaadinIntegration or vaadin4spring to make this easier and each of them have several other advantages.
Related
I am facing an issue with the Vaadin spring annotation #UIScope, defined as follows:
#SpringComponent
#SpringView(name = AdminView.VIEW_NAME)
#UIScope
public class AdminView extends NavigatingView {
...
}
The view is created every time the navigation is opening the view. I would expect that it is created only once, on first time access.
However, if I replace #UIScope with #Scope(UIScopeImpl.VAADIN_UI_SCOPE_NAME) then it works as expected. Did I miss something?
It's related to the order of the #SpringView and #UIScope annotations, as the tutorial and the older wiki page briefly suggest:
// Pay attention to the order of annotations
It's probably related to how and when the annotations are processed. I did not dig that deep into the Vaadin code, but as per the the #SpringView javadoc it puts the view into a view-scope by default. Furthermore, I don't think you require the #SpringComponent annotation because you're already using #SpringView to register it a spring component.
Annotation to be placed on View-classes that should be handled by the SpringViewProvider.
This annotation is also a stereotype annotation, so Spring will automatically detect the annotated classes. By default, this annotation also puts the view into the view scope. You can override this by using another scope annotation, such as the UI scope, on your view class. However, the singleton scope will not work!
In the sample below, you'll find 2 views, the first one with the annotations in the correct order, and the second one with them swapped:
#SpringUI
#SpringViewDisplay
public class MyVaadinUI extends UI implements ViewDisplay {
/* UI */
private Panel springViewDisplay;
#Override
protected void init(VaadinRequest request) {
VerticalLayout mainLayout = new VerticalLayout();
HorizontalLayout buttonLayout = new HorizontalLayout();
springViewDisplay = new Panel();
buttonLayout.addComponent(new Button("1", event -> getNavigator().navigateTo(FirstView.VIEW_NAME)));
buttonLayout.addComponent(new Button("2", event -> getNavigator().navigateTo(SecondView.VIEW_NAME)));
mainLayout.addComponents(buttonLayout, springViewDisplay);
setContent(mainLayout);
}
#Override
public void showView(View view) {
springViewDisplay.setContent((Component) view);
}
/* VIEWS */
#UIScope
#SpringView(name = FirstView.VIEW_NAME)
public static class FirstView extends HorizontalLayout implements View {
public static final String VIEW_NAME = "";
#PostConstruct
private void init() {
System.out.println("Created first view");
addComponent(new Label("First view - " + LocalDateTime.now()));
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// no-op
}
}
#SpringView(name = SecondView.VIEW_NAME)
#UIScope
public static class SecondView extends HorizontalLayout implements View {
public static final String VIEW_NAME = "secondView";
#PostConstruct
private void init() {
System.out.println("Created second view");
addComponent(new Label("Second view - " + LocalDateTime.now()));
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// no-op
}
}
}
As you'll notice in the animation below, when navigating to the second view a new instance is always created, while navigating to the first one will reuse the initial instance:
I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.
Is this possible? I havent found any way to do it.
Static functions could be a way, but they don't have access to the form's controls.
Any ideas?
You can get the controller from the FXMLLoader
FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();
store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:
getFooController().updatePage(strData);
updatePage() can be something like:
// ...
#FXML private Label lblData;
// ...
public void updatePage(String data){
lblData.setText(data);
}
// ...
in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.
Also look the https://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.
Just to help clarify the accepted answer and maybe save a bit of time for others that are new to JavaFX:
For a JavaFX FXML Application, NetBeans will auto-generate your start method in the main class as follows:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Now, all we need to do to have access to the controller class is to change the FXMLLoader load() method from the static implementation to an instantiated implementation and then we can use the instance's method to get the controller, like this:
//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;
#Override
public void start(Stage stage) throws Exception {
//Set up instance instead of using static load() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
Parent root = loader.load();
//Now we have access to getController() through the instance... don't forget the type cast
myControllerHandle = (MyController)loader.getController();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Another solution is to set the controller from your controller class, like so...
public class Controller implements javafx.fxml.Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
// Implementing the Initializable interface means that this method
// will be called when the controller instance is created
App.setController(this);
}
}
This is the solution I prefer to use since the code is somewhat messy to create a fully functional FXMLLoader instance which properly handles local resources etc
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}
versus
#Override
public void start(Stage stage) throws Exception {
URL location = getClass().getResource("/sample.fxml");
FXMLLoader loader = createFXMLLoader(location);
Parent root = loader.load(location.openStream());
}
public FXMLLoader createFXMLLoader(URL location) {
return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}
On the object's loading from the Main screen, one way to pass data that I have found and works is to use lookup and then set the data inside an invisible label that I can retrieve later from the controller class. Like this:
Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData);
This works, but there must be a better way.
I have inherited my view model class from INavigateAware interface as below,
public class ViewModel : INavigationAware
{
public ViewModel()
{
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
// some codes
}
}
And called that view model in the associated view(the page I have been navigated to)
public partial class Page1 : ContentPage
{
ViewModel viewModel;
public Page1()
{
InitializeComponent();
viewModel = new ViewModel();
this.Content = myview; //myview is my control like grid
}
}
Now my problem is when I navigate to this page(Page1), OnNavigateTo() method in ViewModel is not triggered. Please someone helps me how to make trigger OnNavigateTo() method.
Thanks in advance.
First thing first, check if you have AutowireViewModel parameter in your page class set to True.
Second, you should not assign view model yourself, prism will do that for you, when you call PushViewModel
Third there is well known limitation in prism:
https://github.com/PrismLibrary/Prism/issues/563
There is also workaround suggested:
Create interface:
public interface IPageNavigationAware
{
void OnAppearing();
void OnDisappearing();
}
Derive your ViewModel class from this interface.
In the Views code behind:
protected override void OnAppearing()
{
(BindingContext as IPageNavigationAware)?.OnAppearing();
}
protected override void OnDisappearing()
{
(BindingContext as IPageNavigationAware)?.OnDisappearing();
}
The problem with that is that OnAppearing/OnDissapparing are not reliable navigation methods and do not accept parameters, but rather page lifecycle methods. They do not indicate when a page has been navigated to or from. You can have instances where a parent page can be appearing at the same time as multiple child pages are appearing. This will be addressed when Xamarin provides a proper navigation API.
With control level styling I do not need to worry about adding a line of code for every control on every page.
Style Class
public static class Styles
{
#region "Entry"
public static readonly Style Entry_Standard = new Style(typeof(Entry))
{
new Setter {Property = Xamarin.Forms.Entry.BackgroundColorProperty, Value = Color.Red }
};
#endregion
}
Custom Control
public class Custom_Entry : Entry
{
public Custom_Entry()
{
this.Style = Styles.Entry_Standard;
}
}
All of the example documentation from Xamarin though appears to favor setting styles as a property at the page level after object creation either through a page resource or static class reference.
Is there a valid reason for this or is it just that the short examples do not bother to abstract away control styles?
In an application written in Wicket 6, I (as a Wicket newbie) am trying to use an AjaxLink from within a form in order to be able to update the contents of the form.
My use case is the following : through ajax i want to be able to add/remove dynamically controls (file upload controls) to the form.
When using AjaxLink, I am not able to make an ajax call in order to add a new upload file control to the form. On the other hand, when I use AjaxFallbackLink the request gets submitted to the server and a new control is submitted. The only downside in this case is that the values previously selected on the form (the country from country dropdown) are lost.
Can someone explain me how can I cleanly add/remove controls to the form, with ajax, without affecting the other values selected on the form?
Bellow is a simplified version of the form class :
private final class MyForm extends BaseForm<MyFormBean> {
private static final long serialVersionUID = -9021809835323626044L;
private List<Image> sfImages = new ArrayList<>();
private MyForm(final String id, Sample sample) {
super(id, new CompoundPropertyModel<>(new MyFormBean(sample)));
setOutputMarkupId(true);
setMarkupId(id);
setMultiPart(true);
addWithFeedbackContainerAndLabel(new CountryDropDownChoice("country").setRequired(true));
final WebMarkupContainer ajaxContainer = new AjaxUpdatableContainer("ajaxContainer");
add(ajaxContainer);
ajaxContainer.add(new ListView<Image>("sfImages", sfImages) {
#Override
public void populateItem(final ListItem<Image> listItem) {
listItem.add(new UploadImagePanel("sfImage"));
}
});
add(new AjaxLink("addSFImage")
{
private static final long serialVersionUID = 974013580329804810L;
#Override
public void onClick(AjaxRequestTarget target) {
System.out.println("Ajax magic");
MyForm.this.sfImages.add(new DynamicImage("53a4c88f78306456988af612"));
if (target != null) {
target.add(ajaxContainer);
}
}
});
Button button = new SaveButton("saveButton");
add(button);
}