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.
Related
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;
}
};
I am implementing the frontend of an application in GWT (see attached picture) and I have view class which is getting bigger as more widgets are added to the frontend.
As stated in GWT tutorial, the view class must implement the Display interface of the presenter class. y problem is I have a lot a methods in that interface and as I implement them in the view class, it becomes too big. That's why I would like to refactor the code to reduce the size of the view class by implementing those methods in others
classes and reference them where needed in the view class;for instand by grouping them per group box (one class per group box).
Below is a sample code: Note that in the real application we have more widgets per group box.
The problem I am facing will be well explained as you read through the whole posting because I will be adding more details.
code to be refactored:
ContactPrewsenter.java
public class ContactPresenter {
public interface Display
{
void methodA();
void methodB();
void methodC();
void methodD();
void methodE();
void methodF();
.
.
.
void methodM();
}
public ContactPresenter()
{
//Some stuff here
}
......
......
#Override
public void bind(){
//Some stuff here
}
}
ContactView.java:
public class ContactView implements ContactPresenter.Display
{
private final Listbox listBoxA;
private final Listbox listBoxB;
private final Listbox listBoxC;
private final Listbox listBoxD;
private final Listbox listBoxE;
private final Listbox listBoxF;
private final Listbox listBoxG;
private final Listbox listBoxH;
private final Listbox listBoxI;
private final Listbox listBoxJ;
private final Listbox listBoxK;
private final Listbox listBoxL;
private final Listbox listBoxM;
public ContactView()
{
listBoxA = new ListBox();
listBoxB = new ListBox();
VerticalPanel vPanel1= new VerticalPanel();
vPanel1.add(listBoxA);
vPanel1.add(listBoxB);
GrooupBox groupBox1 = new GroupBox();
groupBox1.add(vPanel1);
listBoxC = new ListBox();
listBoxD = new ListBox();
VerticalPanel vPanel2 = new VerticalPanel();
vPanel2.add(listBoxC);
vPanel2.add(listBoxD);
GrooupBox groupBox2 = new GroupBox();
groupBox2.add(vPanel2);
listBoxE = new ListBox();
listBoxF = new ListBox();
VerticalPanel vPanel3 = new VerticalPanel();
vPanel3.add(listBoxE);
vPanel3.add(listBoxF);
GrooupBox groupBox3 = new GroupBox();
groupBox3.add(vPanel3);
listBoxE = new ListBox();
listBoxF = new ListBox();
VerticalPanel vPanel4 = new VerticalPanel();
vPanel4.add(ListBoxE);
vPanel4.add(ListBoxF);
....
GrooupBox groupBox3 = new GroupBox();
groupBox3.add(vPanel4);
listBoxG = new ListBox();
listBoxH = new ListBox();
....
VerticalPanel vPanel = new VerticalPanel();
vPanel.add(ListBoxG);
vPanel.add(ListBoxH);
....
GrooupBox groupBox4 = new GroupBox();
groupBox4.add(vPanel);
......
//create Horizontal/vertical panels, docklayout panel as well, to position the group boxes on the gui
....
}
#Override
void methodA(){
//uses listBoxA
}
#Override
void methodB(){
//used listBoxB
}
#Override
void methodC(){
//uses listBoxC
}
#Override
void methodD(){
//uses listBoxD
}
#Override
void methodE(){
//uses listBoxE
}
#Override
void methodF(){
//uses listBoxF
}
#Override
void methodG(){
//uses listBoxG
}
#Override
void methodH(){
//uses listBoxH
}
.
.
.
#Override
void methodM(){
//uses listBoxM
}
}
I have tried as follows:
ContactPreseter.java
public class ContactPresenter
{
public interface Display extends groupBox1View.Display, groupBox2View.Display, groupBox3View.Display, groupBox4View.Display
{
}
}
preseter classes of each group box
public class groupBox1Presenter
{
public interface Display
{
void methodA();
void methodB();
}
}
public class groupBox2Presenter
{
public interface Display
{
void methodC();
void methodD();
}
}
public class groupBox3Presenter
{
public interface Display
{
void methodE();
void methodF();
}
}
public class groupBox4Presenter
{
public interface Display
{
void methodG();
void methodH();
}
}
ContactView.java
public abstract class ContactView implements ContactPresenter.Display
{
// adds group boxes to horizontal/vertical panels, and docklayout panel
}
Below are the view classes for each group box:
But here I eclipse forces me to implement all the methods of the interface ContactPresenter.Display in each of these classes whereas , I wanted it to be the way you see implemented here.
I was wondering if there were a way to play with access modifiers in order to achieve that ? If not, please I would you to help with ideas how to do it ?
public groupBox1View extends ContactView implements groupBox1Presenter
{
public groupBox1View()
{
}
#Override
void methodA(){
//uses listBoxA
}
#Override
void methodB(){
//used listBoxB
}
}
public groupBox2View extends ContactView implements groupBox2Presenter
{
public groupBox2View()
{
}
#Override
void methodC(){
//uses listBoxC
}
#Override
void methodD(){
//used listBoxD
}
}
public groupBox3View extends ContactView implements groupBox3Presenter
{
public groupBox3View()
{
}
#Override
void methodE(){
//uses listBoxE
}
#Override
void methodF(){
//used listBoxF
}
}
public groupBox4View extends ContactView implements groupBox4Presenter
{
public groupBox4View()
{
}
#Override
void methodG(){
//uses listBoxG
}
#Override
void methodH(){
//used listBoxH
}
}
You are right, your view is growing too big. You need to cut it into components which are handling their own concerns.
The editor framework will prove helpful but has it's own caveats.
In the end, you have one presenter, working with the whole thing, but only reading and writing one contact object.
You build your view from multiple components, each may have it's own presenter and is responsible for one part of your large contact object.
An example: Instead of running 10 listboxes of generic type, make that 10 semantically different components, responsible for selection of different types of data: AgeListBox, CityListBox, FooListBox, BarListBox.
This will seperate the data provisioning for the boxes out of your central presenter, and into the specific presenters for each listbox.
Start at the lowest level and combine editing views for each semantic unit and combine them to larger return objects:
NameEditor, AgeEditor, FooEditor, BarEditor are combined into an AddressEditor, which assembles with a CVEditor into something bigger until you finally arrive at the contact level.
I hope this makes sense to you.
UPdate: You asked for code, let's try some pseudocode:
Let's say you have a profile you want to edit. It contains of
the user's personal data
contains the user address
a bunch of email or mobile addresses
an image or connection to Gravatar
payment information
the list of tags the user is interested in
the list of channels he subscribed
Newsletter/marketing information
public class UserProfile {
PersonalData person;
List<NewsTopic> topicsOfInterest;
List<NewsChannel> subscriptions;
MarketingInfo marketingInfo;
// bean stuff, constr, equals etc.
}
public class PersonalData {
String name;
String firstName;
List<ContactDevice>phoneAndMailList;
ImageContainer userImage;
BankAccount paymentData;
}
You get the idea, I guess...
You can now write ONE view class, detailing all the information you see here, resulting in a monolitic monster view and the matching monster presenter. Or you follow the advice in the gwtproject and cut the view in small as possible pieces (components). That is, subviews and presenters that form a hierarchy, matching the one of your UserProfile class. This is basically what the editor framework is really good at.
In the editor fw, the views are called "Editors" (makes sense), and they get fed the data from top editor down to the smallest part by an EditorDriver class. GWT will generate most of the code for you, which is very cool, but also is not working so perfect, if you have optional parts in the profile.
If we would implement this ourselves, you will build a structure like the following (I avoid the "Editor" and replaced by "Dialog"):
public class UserProfileDialogView extends Component implements HasValue<UserProfile> {
// subviews
#UiField
PersonalDataDialog personDataDlg;
#UiField
UserTopicListDialog topicListDlg;
#UiField
SubscriptionListDialog subscriptionListDlg;
#UiField
MarketingInfoDialog marketingInfoDlg;
#Overwrite
public UserProfile getValue() {
// we do not need to copy back the data from the sub-dialogs, because we gave them the values from the profile itself.
// Beware, substructures cannot be null in such a case!
return userProfile;
}
#Ovewrite
public void setValue(UserProfile profile) {
this.userProfile = profile;
// option one: manually distribute the profile parts
personDataDlg.getPresenter().setValue(profile.getPerson());
topicListDlg.getPresenter().setValue(profile.getTopicsOfInterest());
subscriptionListDlg.getPresenter().setValue(profile.getSubscriptions());
// option two: use eventbus and valuechanged event, dialogs are
}
}
There is now a variance of concerns: Who will set the value in the sub-dialogs. You can forward to the presenter of the sub-dialog, or you set it directly in the sub-dialog.
Anyway, what should get clear to you now, is that you do not have only one presenter to rule all parts of the view. You need to split the presenters to be responsible for one subview each. What I found useful in such a large dialog tree, was to have a separate model class, that keeps the object currently beeing edited and provides change communication logic for other dialogs. For example, if you add a list of topics, and you add one topic, the dialog for the channel subscription selection may want to know, that there is now one more topic to be shown in the topic-filter.
My code looks like this
html
< div wicket:id="metroEthernetChildchkLeft">
java code
initializing CheckBoxMultipleChoice in constructor and then later setting the values of list and model using setter methods
class <some name>
private CheckBoxMultipleChoice<String> metroEthernetChildchkLeft;
<constructor>()
{ metroEthernetChildchkLeft = new CheckBoxMultipleChoice<String>("metroEthernetChildchkLeft");
metroEthernetChildchkLeft.setMarkupId("metroEthernetChildchkLeftId");
metroEthernetChildchkLeft.add(AttributeModifier.prepend("load", "javascript:addMargin(metroEthernetChildchkLeftId);"));
metroEthernetChildchkLeft.setEnabled(false);
commentTechSpeedMetroEthernetListView.add(metroEthernetChildchkLeft);
add(new IndicatingAjaxButton("submitChoiceCmd")
{
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form< ? > form)
{
//// >>>>>>>> updated model value is not coming here <<<<<<
meSpeedSelectLeft = (ArrayList<String>) metaCommentTechSpeedBean.getMeSpeedSelectLeft();
});
}
method()
{
meSpeedSelectLeft = (ArrayList<String>) metaCommentTechSpeedBean.getMeSpeedSelectLeft();
leasedLineChildDivLeft.setDefaultModel(new PropertyModel(metaCommentTechSpeedBean, "llSpeedSelectLeft"));
leasedLineChildDivLeft.setChoices(llSpeedListLeft);
}
i am not able to get checked values [array list of selected checkboxes] in submit method {located in constructor}
Updated :
<div wicket:id="metroEthernetChildchkLeft"></div>
public class MetaCommentTechSpeedChoiceForm extends OForm<MetaCommentTechSpeedBean>
{
private CheckBoxMultipleChoice<String> metroEthernetChildchkLeft;
public MetaCommentTechSpeedChoiceForm(String id)
{
super(id);
metroEthernetChildchkLeft = new CheckBoxMultipleChoice<String>("metroEthernetChildchkLeft");
metroEthernetChildchkLeft.setMarkupId("metroEthernetChildchkLeftId");
metroEthernetChildchkLeft.add(AttributeModifier.prepend("load", "javascript:addMargin(metroEthernetChildchkLeftId);"));
metroEthernetChildchkLeft.setEnabled(false);
commentTechSpeedMetroEthernetListView.add(metroEthernetChildchkLeft);
add(new IndicatingAjaxButton("submitChoiceCmd")
{
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form< ? > form)
{
meSpeedSelectLeft = (ArrayList<String>) metaCommentTechSpeedBean.getMeSpeedSelectLeft(); //// >>>>>>>> updated model value is not coming here <<<<<<
});
}
public void formFunction(final MetaCommentCreationBean metaCommentCreationBean, final Component basicInfoContainer, final Component techSpeedSettingsContainer)
{
meSpeedSelectLeft = (ArrayList<String>) metaCommentTechSpeedBean.getMeSpeedSelectLeft();
leasedLineChildDivLeft.setDefaultModel(new PropertyModel(metaCommentTechSpeedBean, "llSpeedSelectLeft"));
leasedLineChildDivLeft.setChoices(llSpeedListLeft);
}
the problem occurred due to this line
metroEthernetChildchkLeft.setEnabled(false);
i disabled the control and enabling it on frontend using javascript.
Wicket still thinks that the control is disabled and hence not updating the model object.
So, I am having a very difficult time explaining my problem, I hope this is clear. Please tell me if I can clarify anything a little bit better!
I have a GWT web application that is using gwt-dnd to drag and drop widgets. I have Notecard objects (like 3x5 note cards) that extend AbsolutePanel and have a title and number displayed on them.
I have a vertical InsertPanel (a gwt FlowPanel) that Notecard objects are inserted into, one above the next (order matters here). Say there are 5 Notecards contained in the InsertPanel, all aligned vertically. If I pick one of them up, move it to a different index in the InsertPanel, and drop it, the body of the Notecard is dropped where it is supposed to be, but then the title and number are separated from the body of the Notecard and are pushed all the way up to the top of the InsertPanel. If I continue to move Notecards around, all the Notecard bodies function as planed, but all the titles and numbers are stacked on top of each other at the very top of the InsertPanel.
Is there something that I am missing that "glues" the Notecard widget components together? I tried having Notecard extend Composite instead of AbsolutePanel, but I am getting the same behavior.
For reference, I have attached the involved Notecard class below:
Notecard.java:
public class Notecard extends AbsolutePanel implements HasAllMouseHandlers {
private Long ID;
private String storyTitle;
private int points;
private Button dragHandleButton;
private AbstractProject project;
/*
* Constructors
*/
public Notecard( Long Id, String ttl, int pts ) {
this.ID = Id;
this.storyTitle = ttl;
this.points = pts;
setStyleName("Notecard-Wrapper");
setSize("100px", "60px");
Label titleLabel = new Label(storyTitle);
titleLabel.setStyleName("Notecard-TitleLabel");
add(titleLabel, 0, 0);
titleLabel.setSize("96px", "28px");
Label pointsLabel = new Label("" + points);
pointsLabel.setStyleName("Notecard-PointsLabel");
add(pointsLabel, 0, 35);
pointsLabel.setSize("100px", "20px");
dragHandleButton = new Button("");
dragHandleButton.setText("");
dragHandleButton.setStyleName("dragHandleButton");
add(dragHandleButton, 0, 0);
dragHandleButton.setSize("100px", "60px");
Button addTaskButton = new Button("+");
addTaskButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
popupAddTaskPopup();
}
});
addTaskButton.setStyleName("Notecard-AddTaskButton");
addTaskButton.setText("+");
add(addTaskButton, 0, 40);
addTaskButton.setSize("20px", "20px");
}
public void popupAddTaskPopup() {
project.popupAddTaskPopupPanel( ID );
}
/*
* GETTERS & SETTERS
*/
public String getStoryTitle() {
return storyTitle;
}
public void setStoryTitle(String title) {
this.storyTitle = title;
}
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
public Long getID() {
return ID;
}
public Button getDragHandle() {
return dragHandleButton;
}
/*
* Implementation for HasAllMouseHandlers
*/
#Override
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return dragHandleButton.addMouseDownHandler(handler);
}
#Override
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return dragHandleButton.addMouseUpHandler(handler);
}
#Override
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return dragHandleButton.addMouseOutHandler(handler);
}
#Override
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return dragHandleButton.addMouseOverHandler(handler);
}
#Override
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return dragHandleButton.addMouseMoveHandler(handler);
}
#Override
public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler) {
return dragHandleButton.addMouseWheelHandler(handler);
}
I am also using a drop controller that I implemented to handle the onDrop() event. But it just uses the AbstractInsertPanelDropController implementation for onDrop() and then adds some functionality for persistence mechanisms.
I figured out the problem. Turns out it was not at all a GWT or drag and drop thing. It was a CSS issue. My Notecard class, you will notice, is made up of an AbsolutePanel and then its child elements are all positioned absolutely. For the original drawing of the widget, GWT is smart enough to make the child elements be placed inside of the Notecard's wrapper (the AbsolutePanel). But when the Notecard was drag and dropped, it was just standard js, not GWT. Thus, the CSS properties for the child widgets that said:
/* CSS for child widgets */
position: absolute;
left: 0;
top: 0;
were no longer relative to the Notecard wrapper, but instead to the RootPanel. All I did was add a CSS style to the Notecard wrapper to make sure its children were relative to it:
/* CSS for Notecard-Wrapper */
position: relative;
and then everything behaved great.
I figured this out using Firebug for Firefox and then Google'ing around about the CSS issue.
I'm new to ria-development and working with the Ajax Slider example. I can't figure out how to work with javascript events. Here in the example the onValueChanged-event is preimplemented. How do I implement say onchange- or onSlider-event?
All help greatly appreciated!
public abstract class AjaxSlider extends WebMarkupContainer {
private static final long serialVersionUID = 1L;
public AjaxSlider(String id) {
super(id);
super.setOutputMarkupId(true);
}
public JQUIComponentBehaivor<SliderOptions> getSlideBehaviors() {
List behaviors = getBehaviors();
for(Object behavior : behaviors){
if(behavior instanceof SliderBehavior)
return (SliderBehavior) behavior;
}
return null;
}
public abstract void onValueChanged(AjaxRequestTarget target,
int newValue);
#Override
protected void onInitialize() {
super.onInitialize();
AbstractDefaultAjaxBehavior ajaxBehavior =
new AbstractDefaultAjaxBehavior() {
private static final long serialVersionUID = 1L;
#Override
protected void respond(AjaxRequestTarget target) {
String sliderValue = RequestCycle.get().getRequest()
.getParameter("sv");
if (Utils.isNotBlank(sliderValue)) {
onValueChanged(target, Integer.valueOf(sliderValue));
}
}
};
super.add(ajaxBehavior);
super.add(new SliderBehavior(new SliderOptions()
.changeEvent(wicketAjaxGet(
ajaxBehavior,
new MapBuilder<String, Object>().add("sv",
js("ui.value")).build()))));
}
}
The example you gave adds an event handler for the change event. What this event handler does is issueing a GET request to the ajaxBehaviordefined above. The behavior then extracts the slider value from the GET parameters and calls onValueChanged.
You can add another event handler just like this to SliderOptions. For instance:
.slideEvent(
wicketAjaxGet(ajaxBehavior,
new MapBuilder<String, Object>()
.add("sv", js("ui.value")).build()))));
This handler should call the ajax behavior any time the user moves the slider.