Add behavior to Wicket Tab - java

I have a wicket application on a page we have various forms for the same model split into separate tabs. What I need to do is whenever a tab is clicked check to see if a js variable tabDirty is set to true or false. If it is true I would launch a confirm prompt if okay then reset that form and move to the clicked tab. If cancel stay on that tab with keeping current changes.
I have this js for the warning nothing fancy
function warnOnChange(){
if(tabDirty){
decision = confirm('Leave?');
if(decision){
resetTab(); //sets tabDirty back to false
} else {
return false;
}
}
}
I have a super simple wicket behavior
public class WarnChangePromptOnClickBehavior extends Behavior {
#Override
public void bind(Component component) {
component.add(JQBehaviors.mouseClick(EditMerchant.WARN_ON_CHANGE));
}
}
and that behavior is added to the AjaxFallBackLink
AjaxTabbedPanel<CustomAjaxTab> tabbedPanel = new AjaxTabbedPanel<CustomAjaxTab>("tabbedPanel", tabList, new Model<>(0)) {
private static final long serialVersionUID = 1L;
#Override
protected WebMarkupContainer newLink(final String linkId, final int index) {
AjaxFallbackLink<Void> link = new AjaxFallbackLink<Void>(linkId) {
private static final long serialVersionUID = 1L;
#Override
public void onClick(final AjaxRequestTarget target) {
TabbedPanel<CustomAjaxTab> selectedTab = setSelectedTab(index);
CustomAjaxTab tab = tabList.get(index);
if (target != null) {
tab.getPanel(linkId);
target.add(selectedTab);
}
onAjaxUpdate(target);
}
};
link.add(new WarnChangePromptOnClickBehavior());
return link;
}
};
Current behavior with this is that if there is no change the tabs switch no prompt. If there is a change then I get the prompt. If okay tabDirty is reset and go to the next page clearing changes. Issue is that if I click cancel I still navigate to the next tab and lose changes. I know there is something in onClick I need to change but it is just not registering with me.

It is not that easy to intercept the JS event loop, especially when using Ajax requests.
Here is an approach that may do the job:
In warnOnChange() if dirty then call event.preventDefault() and event.stopImmediatePropagation(). This will tell the browser to not follow the link / make an Ajax call. Then show the confirmation dialog as you do now.
If the user presses Cancel then there is nothing more to do
If the use confirms then set dirty to false and do jQuery(event.target).triggerHandler(event.type), i.e. execute the same event (click) on the link. This time it won't be dirty and it will proceed with the Ajax call.

Not sure if this is the appropriate way to do this but I solved my issue like this:
Same old js just slightly modified to return what the user chose:
function warnOnChange(){
decision = true;
if(tabDirty){
decision = confirm('Leave?');
if(decision){
resetTab();
}
}
return decision;
}
Dumped the whole behavior code although I still think it could be used just not sure at the moment...
So to make this all work on the link I override the updateAjaxAttributesof the link with a precondition:
AjaxTabbedPanel<CustomAjaxTab> tabbedPanel = new AjaxTabbedPanel<CustomAjaxTab>("tabbedPanel", tabList, new Model<>(0)) {
private static final long serialVersionUID = 1L;
#Override
protected WebMarkupContainer newLink(final String linkId, final int index) {
AjaxFallbackLink<Void> link = new AjaxFallbackLink<Void>(linkId) {
private static final long serialVersionUID = 1L;
#Override
protected void updateAjaxAttributes( AjaxRequestAttributes attributes ) {
super.updateAjaxAttributes( attributes );
AjaxCallListener ajaxCallListener = new AjaxCallListener();
//very important to use the "return" if not then nothing happens with the response
ajaxCallListener.onPrecondition("return " + WARN_ON_CHANGE);
attributes.getAjaxCallListeners().add( ajaxCallListener );
}
#Override
public void onClick(final AjaxRequestTarget target) {
TabbedPanel<CustomAjaxTab> selectedTab = setSelectedTab(index);
CustomAjaxTab tab = tabList.get(index);
if (target != null) {
tab.getPanel(linkId);
target.add(selectedTab);
}
onAjaxUpdate(target);
}
};
link.add(new WarnChangePromptOnClickBehavior());
return link;
}
};

Related

Preventing URI Navigation in vaadin

I am using the java framework Vaadin to create a web application. There different user levels for this application, and when the user logs in I change the menu depending on their permission level. The problem I am having is preventing a user from visiting a page they are not suppose to by typing in the URI. For example if a user logs in to the application and only has permission to view page one he can still access page two by typing the URI to page two in the browsers search bar.
I was looking into preventing URI navigation, but was unsuccessful in finding out how this is done in Vaadin. So my question is how to prevent URI navigation in Vaadin? If you have a different method of preventing users from accessing pages they are not suppose to please feel free to post that as well.
So far i've come up short and the one post that i've seen about this on stack overflow does not explain it particularly well.
So far this is what my NavigatorUI class looks like:
#SpringUI
public class NavigatorUI extends UI {
public Navigator navigator;
public static final String LOGIN = "";
public static final String MAINPAGE = "main";
public static final String EMPLOYEEPAGE = "employeepage";
public static final String REGISTERPAGE = "registerpage";
public static final String ADDEMPLOYEEPAGE = "addemployeepage";
public static final String ADDEMPLOYERPAGE = "addemployerpage";
private MainSystem main = new MainSystem();
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
Navigator.ComponentContainerViewDisplay viewDisplay = new Navigator.ComponentContainerViewDisplay(layout);
navigator = new Navigator(UI.getCurrent(), viewDisplay);
navigator.addView(LOGIN, new LoginView());
navigator.addView(MAINPAGE, new MainView());
navigator.addView(EMPLOYEEPAGE, new EmployeeView());
navigator.addView(REGISTERPAGE, new RegisterView());
navigator.addView(ADDEMPLOYEEPAGE, new AddEmployeeView());
navigator.addView(ADDEMPLOYERPAGE, new AddEmployerView());
navigator.addViewChangeListener(new ViewChangeListener() {
#Override
public boolean beforeViewChange(ViewChangeEvent event) {
View newView = event.getNewView();
String newViewString= newView.toString();
newViewString = newViewString.substring(0,newViewString.length()-9);
View loginView = new LoginView();
String loginViewString = loginView.toString();
loginViewString = loginViewString.substring(0,loginViewString.length()-9);
boolean result = true;
if (newViewString.equals(loginViewString)){
return result;
}
else if (VaadinSession.getCurrent().getAttribute("role").toString().equals("Admin")||VaadinSession.getCurrent().getAttribute("role").toString().equals("Employee")){
return result;
}
else {
result = false;
}
return result;
}
#Override
public void afterViewChange(ViewChangeEvent event) {
//NO-OP
}
});
}
}
You add a ViewchangeListener to your Navigator instance, that can prevent navigation in the beforeViewChange.
If any listener returns false, the view change is not allowed and afterViewChange() methods are not called.
So you implement that Interface with a check of the current users authorizations against the newView of the event. And in case you want to block that, you return false there.

JPA: perform showDialog() in a new browser tab?

As the title above, I want the behaviour of showDialog() to display on a new browser tab but I don't know whether is there any Java code written for that.
My code:
public class ListSportImagesMethod2Action extends TabBaseAction {
private int row;
#Inject
private Tab tab;
public void execute() throws Exception {
Map sportKey = (Map) tab.getTableModel().getObjectAt(row);
//Map sportKey = (Map) getView().getSubview("sport").getValues();
int sportId = ((Integer) sportKey.get("sportId")).intValue();
sportKey.put("sportId", sportId);
showDialog(); //What should it replace with?
getView().setModelName("Sport");
getView().setValues(sportKey); System.out.println("data============" + getView().getValues());
getView().findObject();
getView().setKeyEditable(false);
getView().setEditable(true);
setControllers("Sport");
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
public Tab getTab() {
return tab;
}
public void setTab(Tab tab) {
this.tab = tab;
}
}
PS: I know I can use a link to redirect but with this method, I fail to get the parent's data. Am still trying.
showDialog() shows a JavaScript dialog, not a browser windows. showDialog() cannot be used to show something in other browser tab, for that use IForwardAction. IForwardAction goes to a new browser windows, but if you use IForwardAction you have to change all the logic of your action because you redirect to other module that has its own state. The OpenXava documentation explains how to manage this last case.

How can I change Page Title periodically and switch the change on/off?

I want to change the title of my page periodically, i.e. add a (*) in front of the current page title and remove it after a couple of seconds. I want to turn this title change on and off in code.
I get and set the page title from:
public static native void setPageTitle(String title) /*-{
$doc.title = title;
}-*/;
public static native String getPageTitle() /*-{
return $doc.title;
}-*/;
But how should I write a function that will change the page title every 300 miliseconds while adding and removing a prefix?
What I tried was:
private void changePageTitle(final String prefix) {
new Timer() {
#Override
public void run() {
String pageTitle =getPageTitle();
if (pageTitle.startsWith(prefix)) {
pageTitle = pageTitle.substring(prefix.length());
}
else {
pageTitle = pageTitle + prefix;
}
setPageTitle(pageTitle);
}
}
}.schedule(300);
}
This does not work. And I do not know how to switch the process on and off?
The Effect should be like in Facebook. When a new message arrive and you are not on the Facebook browser tab, then the tab shows a notification which is blinking.
You have to change schedule(300) by scheduleRepeating(300).
You should use just one instance of Timer or save the last timer to cancel it before creating a new one.
BTW: you dont need to write any JSNI to access the window title, just use Window.getTitle() and Window.setTitle(String)
EDITED:
This should work:
// create just an instance of the timer
final MyUpdateTitleTimer mytimer = new MyUpdateTitleTimer();
// To Start the updater
mytimer.setPrefix("> ");
// To Stop set the prefix to null
mytimer.setPrefix(null);
class MyUpdateTitleTimer extends Timer {
private String prefix;
private String title;
private boolean b;
public void run() {
String s = (b = !b) ? prefix + title : title;
Window.setTitle(s);
}
public void setPrefix(String prefix) {
if (title != null) {
Window.setTitle(title);
}
this.prefix = prefix;
if (prefix == null) {
cancel();
} else {
title = Window.getTitle();
scheduleRepeating(300);
}
}
}

How to access Boolean flag into remote Java Class

I'm working on a JavaFX application which will have several tab panes which I want to set to visible or hidden using check box which will send boolean flag to render or not to render the component.
Check box
final CheckMenuItem toolbarSubMenuNavigation = new CheckMenuItem("Navigation");
toolbarSubMenuNavigation.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
// call here the getter setter and send boolean flag
System.out.println("subsystem1 #1 Enabled!");
}
});
Tab pane which will listen for the boolean property:
public boolean renderTab;
public boolean isRenderTab()
{
return renderTab;
}
public void setRenderTab(boolean renderTab)
{
this.renderTab = renderTab;
}
tabPane.setVisible(renderTab);
The check box and the tab pane are isolated into different Java Classes. I need to send the value of the flag every time when I check or uncheck the flag. Can you tell me how I can send the flag using getter and setter?
EDIT
I tested this code:
final CheckMenuItem toolbarSubMenuNavigation = new CheckMenuItem("Navigation");
toolbarSubMenuNavigation.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
boolean dcd = toolbarSubMenuNavigation.isSelected();
DataTabs nn = new DataTabs();
nn.setRenderTab(dcd);
// call here the getter setter and send boolean flag
System.out.println("subsystem1 #1 Enabled!");
}
});
and
public boolean renderTab;
public boolean isRenderTab()
{
return renderTab;
}
public void setRenderTab(boolean renderTab)
{
this.renderTab = renderTab;
}
But it's not working when I switch the checkbox.
No.
Inorder to get that eithr you need to have a intance or you need to create new intance there.
If you create a new object there it will create a fresh intance,which doesnt helps you any more..
I guess the only way you have is to Make the renderTab as a static field and access there.

Building a Reuseable Wicket Component

So this is not a very general question, but I was hoping some people could give me some pointers on architecture so that I can build the following reusable wicket component.
Here is a rough sketch on skitch:
https://skitch.com/cmagnollay/8sn2s/multitextform
I know, great drawing right? So essentially, this formcomponent (i think this is the right class to use) will be used to add a user defined number of inputs on a form. When the user hits the - button next to a TextInputField it removes that inputField. When they hit the + button, a new blank field is added. Obviously the component will need to use AJAX to update the component when the user clicks the buttons, but my issue is how to structure this. Is this one class? two (one for whole component, one for inputfield with - button), what classes should I be using to do this? I would like the object to be as general as possible to promote reuse. Here is what I have so far:
public class MultiTextInput<T> extends FormComponent<List<T>>
{
private static final long serialVersionUID = 1L;
private final String removeInputButtonName = "removeInputButton";
private final String addInputButtonIdName = "addInputButton";
private int numInputs = 1;
private List<TextField<T>> inputFieldList = new ArrayList<TextField<T>>();
public MultiTextInput(String id, IModel<T> model)
{
super(id);
inputFieldList.add(new TextField<T>("input1", model));
add(inputFieldList.get(0));
addAddInputFieldMarkup();
}
/**
* Adds an "add" button.
*/
private void addAddInputFieldMarkup()
{
Button addInputButton = new Button(this.addInputButtonIdName + numInputs);
addInputButton.add(new AjaxFormComponentUpdatingBehavior("onclick"){
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target)
{
numInputs++;
inputFieldList.add(new TextField<T>("input" + numInputs));
target.add(MultiTextInput.this);
}
});
}
/**
* Adds a "remove" button.
*/
private void addRemoveInputFieldMarkup()
{
Button removeInputButton = new Button(this.removeInputButtonName + numInputs);
removeInputButton.add(new AjaxFormComponentUpdatingBehavior("onclick"){
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget arg0)
{
// TODO Auto-generated method stub
}
});
}
}
As I said, I am just trying to get used to thinking about making Wicket components. I have a lot of experience with OO, but just not particularly with wicket. Thanks for any help and direction!
I guess the easiest way to implement the desired behavior would be to use a ListView backed by a List. And just reload after the add/remove button has been pressed.
Here is a code scribble (not tested)
public abstract class MultiTextPanel<T> extends Panel {
public MultiTextPanel(String id, IModel<ArrayList<T>> model) {
super(id, model);
final Form<ArrayList<T>> multiTextForm = new Form<ArrayList<T>>("multiTextForm", model);
add(multiTextForm);
final ListView<T> listView = new ListView<T>("listView", model) {
#Override
protected void populateItem(final ListItem<T> item) {
// TODO Auto-generated method stub
TextField<T> textField = new TextField<T>("textField", item.getModel());
add(textField);
AjaxSubmitLink removeButton = new AjaxSubmitLink("removeButton", multiTextForm) {
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
multiTextForm.getModelObject().remove(item.getModelObject());
target.addComponent(multiTextForm);
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
//errors should be ignored, we shoudlnt validate in our form, so this shouldnt happen anyway
multiTextForm.getModelObject().remove(item.getModelObject());
target.addComponent(multiTextForm);
}
};
add(removeButton);
}
};
add(listView);
AjaxSubmitLink addButton = new AjaxSubmitLink("addButton", multiTextForm) {
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
//errors should be ignored, we shoudlnt validate in our form, so this shouldnt happen anyway
multiTextForm.getModelObject().add(createNewT());
target.addComponent(multiTextForm);
}
#Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
multiTextForm.getModelObject().add(createNewT());
target.addComponent(multiTextForm);
}
};
add(addButton);
}
public abstract T createNewT();}
Basic html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.sourceforge.net/" xml:lang="en" lang="en">
<wicket:panel>
<form wicket:id="multiTextForm">
<wicket:container wicket:id="listView">
<input type="text" wicket:id="textField" />
<a wicket:id="removeButton">-</a>
</wicket:container>
</form>
<a wicket:id="addButton">+</a>
</wicket:panel>
The only special thing I've done with this is to put a form around the ListView so we are able to just submit inside the Panel we've created (validation is most likely not needed at this stage and should be done in the form that saves the screen).
The downside with this implementation is that you will always reload the complete form and therefore create a lot of overhead. Only 1 row is added/removed but n(-/+)1 are re-rendered.

Categories