I choose Vaadin for building my very simple UI within the spring boot application.
I have:
#Route
#PWA(name = "Signing certificates manager", shortName = "CertMgr")
public class MainView extends VerticalLayout {
public MainView() {
Tab tab1 = new Tab("Certificates");
Tab tab2 = new Tab("Users");
Tabs tabs = new Tabs(tab1, tab2);
CertView certView = new CertView();
certView.setVisible(false);
UserView userView = new UserView(addUsers());
userView.setVisible(true);
Map<Tab, Component> tabsToPages = new HashMap<>();
tabsToPages.put(tab1, certView);
tabsToPages.put(tab2, userView);
Div pages = new Div(certView, userView);
Set<Component> pagesShown = Stream.of(userView)
.collect(Collectors.toSet());
tabs.addSelectedChangeListener(event -> {
pagesShown.forEach(page -> page.setVisible(false));
pagesShown.clear();
Component selectedPage = tabsToPages.get(tabs.getSelectedTab());
selectedPage.setVisible(true);
pagesShown.add(selectedPage);
});
add(tabs, pages);
}
private List<User> addUsers() {
return new LinkedList<User>() {{
add(new User("qewr", "asdf", "xzcv"));
}};
}
}
and
public class UserView extends VerticalLayout {
Grid<User> grid;
public UserView(List<User> users) {
grid = new Grid<>(User.class);
grid.setItems(users);
grid.addColumn(User::getMail).setHeader("mail");
grid.addColumn(User::getName).setHeader("name");
grid.addColumn(User::getPass).setHeader("pass");
grid.addSelectionListener(event -> {
List<User> selected = (List<User>) event.getAllSelectedItems();
add(new UserRow(selected.get(0)));
});
add(grid);
}
}
and the result of this is a view like this:
so you can see, the grid has no width and is too long (I have only one user there)
I am new to Vaadin so I am surely doing something wrong, but basically this is vaadin tabs handling from the samples.
My questions are:
how do I draw the grid properly with my current setup?
is it possible to display a view from completely different page when clicking on tab with that page would have it's own context and could use spring's DI? like to have autonomous page and not coupled objects like I have now. or advice the best pattern to handle this with vaadin, please.
Vaadin: 12.0.7
Spring-Boot: 2.1.2.RELEASE
Hope you get my point.
Thanks for advices!
just do
Div pages = new Div(certView, userView);
pages.setSizeFull();
Related
I'm trying to refresh grid after adding an row into it, but it's not working. Here is my Event Listener on my UI Code : Edited Full code.
Grid<TransactionModel> transactionData = new Grid<>(TransactionModel.class);
try {
// transactionData.setItems(transactionServices.getTransactionTable());
List<TransactionModel> transactionList = transactionServices.getTransactionTable();
ListDataProvider<TransactionModel> transactionDataProvider = new ListDataProvider<>(transactionList);
transactionData.setDataProvider(transactionDataProvider);
transactionData.setColumns("id", "transactionTimestamp", "srcAccountId", "dstAccountId", "amount");
Grid.Column<TransactionModel> idColumn = transactionData.getColumnByKey("id");
Grid.Column<TransactionModel> srcAccountIdColumn = transactionData.getColumnByKey("srcAccountId");
Grid.Column<TransactionModel> dstAccountIdColumn = transactionData.getColumnByKey("dstAccountId");
HeaderRow filterRow2 = transactionData.appendHeaderRow();
TransactionFilterModel transactionFilterModel = new TransactionFilterModel();
transactionDataProvider.setFilter(transaction -> transactionFilterModel.find(transaction));
// Filter srcAccountId
TextField idField = new TextField();
idField.addValueChangeListener(event -> {
transactionFilterModel.setId(event.getValue());
transactionDataProvider.refreshAll();
});
idField.setValueChangeMode(ValueChangeMode.EAGER);
filterRow2.getCell(idColumn).setComponent(idField);
idField.setSizeFull();
idField.getElement().setAttribute("focus-terget", "");
// Filter srcAccountId
TextField srcAccountIdField = new TextField();
srcAccountIdField.addValueChangeListener(event -> {
transactionFilterModel.setSrcAccountId(event.getValue());
transactionDataProvider.refreshAll();
});
srcAccountIdField.setValueChangeMode(ValueChangeMode.EAGER);
filterRow2.getCell(srcAccountIdColumn).setComponent(srcAccountIdField);
srcAccountIdField.setSizeFull();
srcAccountIdField.getElement().setAttribute("focus-terget", "");
// Filter dstAccountId
TextField dstAccountIdField = new TextField();
dstAccountIdField.addValueChangeListener(event -> {
transactionFilterModel.setDstAccountId(event.getValue());
transactionDataProvider.refreshAll();
});
dstAccountIdField.setValueChangeMode(ValueChangeMode.EAGER);
filterRow2.getCell(dstAccountIdColumn).setComponent(dstAccountIdField);
dstAccountIdField.setSizeFull();
dstAccountIdField.getElement().setAttribute("focus-terget", "");
transactionData.setWidth("50%");
} catch (JsonProcessingException | EndpointException ex) {
Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ex);
}
// Event Listener
submitButton.addClickListener(e -> {
System.out.println("Submitted !");
AccountModel submittedModel = new AccountModel();
if (accountModelBinder.writeBeanIfValid(submittedModel)) {
try {
accountServices.registerAccount(submittedModel);
accountIdTextField.clear();
nameTextField.clear();
addressTextField.clear();
birthDateDatePicker.clear();
allowNegativeBalanceButtonGroup.clear();
} catch (EndpointException | JsonProcessingException ez) {
Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ez);
}
}
accountData.getDataProvider().refreshAll(); // <- REFRESH
});
And for service I'm using rest, here is the accountservice code:
public List<AccountModel> getAccountTable() throws JsonProcessingException, EndpointException {
List<AccountModel> datalog = new JsonResponseReader(restMockvaEndpoint.send(new EndpointRequestBuilder()
.method("GET")
.resource("/account")
.build()
)).getContentTable(AccountModel.class).getData();
return datalog;
}
public AccountModel registerAccount(AccountModel accountModel) throws JsonProcessingException, EndpointException{
AccountModel account = new JsonResponseReader(restMockvaEndpoint.send(new EndpointRequestBuilder()
.method("POST")
.content(Json.getWriter().writeValueAsBytes(accountModel), MediaType.APPLICATION_JSON)
.resource("/account")
.build())).getContentObject(AccountModel.class);
return account;
}
Edited : Add registerAccount.
The problem is when I click submitButton for adding new data, the grid doesn't refresh. Any ideas?
For this to work with a ListDataProvider, you would have to modify the underlying list (add/remove items).
Now that you call refreshAll(), it just reads the list you passed again, and as it still contains the same items, nothing changes. It does not know to fetch the items from your service again.
There are a few solutions that I can think of:
1. Manually add the new item to the list (it will then appear at the end of the grid):
accountList.add(submittedModel);
...
// This instructs the grid to read the list again
accountData.getDataProvider().refreshAll();
If your accountServices.registerAccount method returns the newly saved item, you might want to add that one instead.
2. Set the items again
You could fetch the items again and set a new data provider. You can just use setItems(...) then, which uses a ListDataProvider under the hood.
// Run this both when first creating the grid, and again after the new item is saved.
// This time you don't need to call refreshAll()
List<AccountModel> accountList = accountServices.getAccountTable();
accountData.setItems(accountList);
3. Use a lazy data provider
When you use a lazy data provider, for example from callbacks, then calling refreshAll() executes those callbacks again to fetch new items.
In this case you need to implement the needed service methods, and it requires a bit more work if you need sorting or filtering.
this.setDataProvider(DataProvider.fromCallbacks(
query -> myAccountService.getAccounts(query.getOffset(), query.getLimit()).stream(),
query -> myAccountService.countAccounts()
));
I have a simple Spring Boot application with Vaadin for UI and Spring Boot Security.
What I'm trying to achive is simple navigation between components from login page to main view.
this is my security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
// Not using Spring CSRF here to be able to use plain HTML for the login page
http.csrf().disable()
.authorizeRequests()
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
.and().formLogin().loginPage(LOGIN_URL).permitAll().loginProcessingUrl(LOGIN_PROCESSING_URL)
.failureUrl(LOGIN_FAILURE_URL)
.successHandler(new SavedRequestAwareAuthenticationSuccessHandler())
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_URL);
}
this is my LoginView:
#Route("login")
#UIScope
#SpringComponent
public class LoginView extends VerticalLayout {
/**
* AuthenticationManager is already exposed in WebSecurityConfig
*/
#Autowired
private AuthenticationManager authManager;
private LoginOverlay loginOverlay;
public LoginView() {
loginOverlay = new LoginOverlay();
loginOverlay.addLoginListener(this::authenticate);
loginOverlay.setOpened(true);
LoginI18n i18n = LoginI18n.createDefault();
i18n.setAdditionalInformation("Welcome");
loginOverlay.setI18n(i18n);
add(loginOverlay);
}
private void authenticate(AbstractLogin.LoginEvent e) {
try {
Authentication auth = authManager.authenticate(
new UsernamePasswordAuthenticationToken(e.getUsername(), e.getPassword()));
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
if (auth.isAuthenticated())
getUI().ifPresent(ui -> ui.navigate(MainView.class));
} catch (Exception ex) {
loginOverlay.setError(true);
}
}}
and the MainView:
#Route("main")
public class MainView extends VerticalLayout implements AfterNavigationObserver {
private final CertView certView;
private final UserView userView;
public MainView(CertView certView, UserView userView) {
this.certView = certView;
this.userView = userView;
}
private void createMain() {
Tab tab1 = new Tab("Certificates");
Tab tab2 = new Tab("Users");
Tabs tabs = new Tabs(tab1, tab2);
certView.setVisible(true);
userView.setVisible(false);
Map<Tab, Component> tabsToPages = new HashMap<>();
tabsToPages.put(tab1, certView);
tabsToPages.put(tab2, userView);
Div pages = new Div(certView, userView);
pages.setSizeFull();
Set<Component> pagesShown = Stream.of(certView)
.collect(Collectors.toSet());
tabs.addSelectedChangeListener(event -> {
pagesShown.forEach(page -> page.setVisible(false));
pagesShown.clear();
Component selectedPage = tabsToPages.get(tabs.getSelectedTab());
selectedPage.setVisible(true);
pagesShown.add(selectedPage);
});
add(tabs, pages);
}
#Override
public void afterNavigation(AfterNavigationEvent afterNavigationEvent) {
createMain();
}
}
CertView and UserView are #UIScoped #SpringComponents that have some DAO injected and are getting data and setting it's components right in theire cosntructor.
Now what happens when authenticate and authManager.authenticate of Login view gets called is view is being routed to the MainView which I can tell by seeing constructor being called, url changes but nothing get's rendered. Strange is that when I set the breakpoint in MainView's constructor page renders successfully.
I'm quite new to Vaadin and I don't know what should the correct navigation look like so you can comment better way of doing this, but actually I would like to stay as simple as possible.
So how do I navigate correctly or get mainView's content rendered at the correct lifecycle event?
Vaadin: 13.0.1
I had the same problem. I solved it with closing the loginOverlay before navigating to another route.
if (auth.isAuthenticated())
{
loginOverlay.close(); // <-- add this line!
getUI().ifPresent(ui -> ui.navigate(MainView.class));
}
I am trying to develop a demo app using JavaFX where I have 2 controllers MainController and AddUserController, each of them has its own .fxml file, main.fxml contains table that displays the list of users, and a button that opens the window for adding user with some fields. So the list of users is contained in a in-memory DB.
So when adding an user the table isn't filled with the list.
MainController.java
public void setTestUser(){
List<Users> users = new ArrayList();
Users user = new Users();
user.setName("Name");
user.setSurname("Surname");
users.add(user);
name.setCellValueFactory(new PropertyValueFactory<Users, String>("name"));
surname.setCellValueFactory(new PropertyValueFactory<Users, String>("surname"));
usersTable.setItems(FXCollections.observableArrayList(users));
}
AddUserController.java
private void handleAddUser(ActionEvent event) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.load(getClass().getResource("/fxml/main.fxml").openStream());
MainController mainController = fxmlLoader.getController();
Users user = new Users();
user.setName(name.getText());
user.setSurname(surname.getText());
user.setDateOfBirth(dateOfBirth.getValue().toString());
usersService.add(user);
mainController.populateUsersTable();
}
After adding the user the table isn't updated, but if I try to usersTable.getItems(); the list is there, what's the problem so far?
// UPDATE:
Trying to execute this from MainController.java and it is working OK
public void setTestUser(){
List<Users> users = new ArrayList();
Users user = new Users();
user.setName("Name");
user.setSurname("Surname");
users.add(user);
name.setCellValueFactory(new PropertyValueFactory<Users, String>("name"));
surname.setCellValueFactory(new PropertyValueFactory<Users, String>("surname"));
usersTable.setItems(FXCollections.observableArrayList(users));
}
The reason might be that you create a new controller on a new pane each time you handle an "add user" event. This is plainly wrong, as changes to it don't affect the pane which is actually displayed.
Instead, you should get the controller when you load your pane, store it somewhere and refer to it whenever you need it.
FXMLLoader myLoader = new FXMLLoader(
getClass().getResource(screen.getFile()),
resources);
Parent loadScreen = myLoader.load();
XYController myScreenController = myLoader.getController();
Following the example at http://www.wobblycogs.co.uk/index.php/computing/jee/49-dynamic-dashboard-with-primefaces im trying to create a dynamic dashboard.
Copying the code works 100% perfeclty, but im trying to use it with my own implementation.
When i load my index page it dosnt show any panels or the dashboard at all. When i navigate to a differet page, and come back the elements appear!
I have no idea why the dashboard is not displayed on initial page load.
Im working with Jboss 5.1, EJB 3.0, JSF 2.0 and Primefaces 3.4.
This is the top of my controller class:
#Model
#ViewController(viewId = "/pages/index.xhtml")
public class MonitorController implements Serializable, ViewInitializer, ViewFinalizer {
#Inject
private transient MonitorService monitorService;
#Inject
private transient MonitorUserService monitorUserService;
#Inject
private transient MonitorView view;
#Inject
private transient Conversation conversation;
#Override
public void initializeView() {
if (conversation.isTransient()) {
conversation.setTimeout(1800000);
conversation.begin();
}
MonitorUser user = monitorUserService.findOrCreateUser();
view.setUser(user);
List<MonitorElement> elements = monitorService.findMonitorElementsByUser();
view.setElements(elements);
view.setUsersMonitorElements(elements);
createDashBoard();
}
This is my method for creating the dashboard, adding panels and setting the column widgets:
public void createDashBoard() {
final int DEFAULT_COLUMN_COUNT = 3;
int columnCount = DEFAULT_COLUMN_COUNT;
Dashboard dashboard;
FacesContext fc = FacesContext.getCurrentInstance();
Application application = fc.getApplication();
dashboard = (Dashboard) application.createComponent(fc, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer");
dashboard.setId("dashboard");
DashboardModel model = new DefaultDashboardModel();
for( int i = 0; i < columnCount; i++ ) {
DashboardColumn column = new DefaultDashboardColumn();
model.addColumn(column);
}
dashboard.setModel(model);
view.setModel(dashboard.getModel());
view.setDashboard(dashboard);
int index = 0;
for( MonitorElement i : view.getUsersMonitorElements()) {
Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer");
panel.setId("id" + i.getId());
panel.setHeader("Dashboard Component " + i.getApplicationName());
panel.setClosable(true);
panel.setToggleable(true);
dashboard.getChildren().add(panel);
DashboardColumn column2 = model.getColumn(index%columnCount);
column2.addWidget(panel.getId());
HtmlOutputText text = new HtmlOutputText();
text.setValue(i.getCount() + "" );
panel.getChildren().add(text);
index++;
}
view.setModel(dashboard.getModel());
view.setDashboard(dashboard);
}
I am implementing the dashboard on my index page using:
<p:dashboard id="dynamic_dashboard3"
binding="#{monitorView.dashboard}">
</p:dashboard>
I'm trying to build grid with build in column filtering (using sencha gxt), here is my code:
public Grid<Stock> createGrid() {
// Columns definition
ColumnConfig<Stock, String> nameCol = new ColumnConfig<Stock, String>(props.name(), 100, "Company");
// Column model definition and creation
List<ColumnConfig<Stock, ?>> cl = new ArrayList<ColumnConfig<Stock, ?>>();
cl.add(nameCol);
ColumnModel<Stock> cm = new ColumnModel<Stock>(cl);
// Data populating
ListStore<Stock> store = new ListStore<Stock>(props.key());
store.addAll(TestData.getStocks());
// Grid creation with data
final Grid<Stock> grid = new Grid<Stock>(store, cm);
grid.getView().setAutoExpandColumn(nameCol);
grid.setBorders(false);
grid.getView().setStripeRows(true);
grid.getView().setColumnLines(true);
// Filters definition
StoreFilterField<Stock> filter = new StoreFilterField<Stock>() {
#Override
protected boolean doSelect(Store<Stock> store, Stock parent, Stock item, String filter) {
// Window.alert(String.valueOf("a"));
String name = item.getName();
name = name.toLowerCase();
if (name.startsWith(filter.toLowerCase())) {
return true;
}
return false;
}
};
filter.bind(store);
cm.addHeaderGroup(0, 0, new HeaderGroupConfig(filter, 1, 1));
filter.focus();
return grid;
}
My problem is: after I run this code, I cannot write anything to filter input, I'm using test data and classes (Stock.java and StockProperties.java) from this example: http://sencha.com/examples-dev/#ExamplePlace:filtergrid
I try to put allert in doSelect method to check if this function was called, but it wasn't.
Any idea will be welcome. Thanks.
I was able to make your code work. I observed that there were compiler errors in the code for StoreFilterField class. Here is the code that filters the grid based on the values in the first column, that is, name field in the Stock model.
StoreFilterField<Stock> filter1 = new StoreFilterField<Stock>() {
#Override
protected boolean doSelect(Store<Stock> store, Stock parent, Stock record, String property, String filter) {
String name = record.get("name");
name = name.toLowerCase();
if (name.startsWith(filter.toLowerCase())) {
return true;
}
return false;
}
};
filter1.bind(store);
Btw, I tested this with GXT 2.2.5 and GWT 2.4.
Thanks,
Ganesh
I solve this problem according to this paper http://www.sencha.com/forum/archive/index.php/ … but I replace disableTextSelection(false) with setAllowTextSelection(true);