I have a ListView which shows some panels. Because this ListView can be long, I want to make it collapsible.
For this purpose I added a WebMarkupContainer:
final WebMarkupContainer wmc = new WebMarkupContainer("listViewContainer");
wmc.setOutputMarkupId(true);
this.add(wmc);
This is how the ListView is created:
final ListView<String> examListViewSignedUp = new ListView<String>("examLVsu", signedUpExams) {
private static final long serialVersionUID = -2828116887162006658L;
protected void populateItem(ListItem<String> item) {
String examId = item.getModelObject();
item.add(new ExamPanel("exam1", examId));
}
};
wmc.add(examListViewSignedUp);
Finally, I add the AjaxLink. That is, in fact, a "[-]" if the list is expanded and a "[+]" otherwise. I "expand"/"collapse" the ListView by setting it's visibility via .setVisible(...).
final Model<String> sulink = new Model<String>(esuclt);
wmc.add(new AjaxLink("overviewCollapseLinkSignedUp") {
private static final long serialVersionUID = 8603181768552484977L;
#Override
public void onClick(AjaxRequestTarget target) {
if (examListViewSignedUp.isVisible()) {
examListViewSignedUp.setVisible(false);
sulink.setObject("[+]");
} else {
examListViewSignedUp.setVisible(true);
sulink.setObject("[-]");
}
target.add(wmc);
}
}.add(new Label("oclt1", sulink)));
Markup:
<wicket:container wicket:id="listViewContainer">
<h1 class="ocl">
<a wicket:id="overviewCollapseLinkSignedUp" href="#"><span wicket:id="oclt1">[?]</span></a>
<wicket:message key="examsHeadingInOverviewSignedUp">Exams - Signed Up</wicket:message>
</h1>
<span wicket:id="examLVsu"> <span wicket:id="exam1"></span></span>
</wicket:container>
My problem now is that the MarkupContainer isn't refreshed when clicking on the link. Nothing happens, but when I then reload the page, everything looks as expected. I searched for similar problems, but I wasn't able to find my mistake.
You cannot update <wicket:container>'s via Ajax, since they are not present in the final markup.
Use a normal <div wicket:id="listViewContainer"> instead.
Related
I am new in GWT world, previously I was working with JavaScript. I have some web-page where are multiple element. And I need to track which element was clicked using GWT.
I am using GWT 2.8.1
<div class="FileRow">
</div>
<div class="FileRow">
</div>
<div class="FileRow">
</div>
GWT realization
Integer tabIndex = 1, count = 1;
for (final FieldFileInfo info : clientData.getFileInfo()) {
final FlowPanel rowPanel = new FlowPanel();
rowPanel.setStyleName("FileRow");
final HyperlinkPanel fileLink = FileInfoParser.getLinkPanel(info);
fileLink.setStyleName("fileLink");
fileLink.setTabIndex(tabIndex++);
fileLink.setText("");
switch (info.getFileExtension()) {
case "png" :
fileLink.addStyleName("png");
fileLink.getElement().setId("png");
break;
case "jpg" :
fileLink.addStyleName("jpg");
fileLink.getElement().setId("jpg"+count);
break;
case "pdf" :
fileLink.addStyleName("pdf");
fileLink.getElement().setId("pdf");
break;
default :
fileLink.addStyleName("file");
fileLink.getElement().setId("file");
}
rowPanel.add(fileLink);
count++;
}
click realization
Element openPng = Document.get().getElementById("png");
Event.sinkEvents(openPng, Event.ONCLICK);
Event.setEventListener(openPng, new OpenModalHandler());
Now my click realization works only on first element with required ID. However in future there might be several elements with the same id and as a result I could not alert another elements.
Expected result is when I click on first element, GWT using Window.alert() show which element was clicked.
The answer is really simple.
To track which element was clicked I just modified case with:
fileLink.addClickHandler(new ClickHandlerData<Integer>(tabIndex) {
public void onClick(ClickEvent event) {
Window.alert("You clicked on " getData());
}
});
Also implement new class with ClickHandlerData
public abstract class ClickHandlerData<I> implements ClickHandler {
private I data;
public ClickHandlerData(I data) {
this.data = data;
}
public I getData() {
return data;
}
public void setData(I data) {
this.data = data;
}
}
With wicket-dnd, is it possible to use dropTop() / dropBottom() with HTML tables? If so, what should the selector be?
I have an HTML table created via a ListView and have had success with dropCentre("tr"), but this is the only drop option that appears to work. Ideally, I would like to use dropTopAndBottom() and see a horizontal divider between table rows which indicates the drop target.
Update:
Here's the relevant code, simplified for for brevity. The table in question has a label per row and is added to a form.
// Container class for Wicket DND
final WebMarkupContainer dataWrapper = new WebMarkupContainer("dataWrapper");
dataWrapper.add(new WebTheme());
final ListView<BinaryData> data = new ListView<BinaryData>("data", list) {
#Override
protected void populateItem(final ListItem<BinaryData> item) {
final BinaryData data = item.getModelObject();
item.add(new Label("label", data.getLabel()));
}
#Override
protected ListItem<BinaryData> newItem(final int index, final IModel<BinaryData> itemModel) {
final ListItem<BinaryData> item = super.newItem(index, itemModel);
item.setOutputMarkupId(true);
return item;
}
};
dataWrapper.add(data);
dataWrapper.add(new DragSource(Operation.MOVE) {
#Override
public void onAfterDrop(final AjaxRequestTarget target, final Transfer transfer) {
}
}.drag("tr"));
dataWrapper.add(new DropTarget(Operation.MOVE) {
#Override
public void onDrop(final AjaxRequestTarget ajaxTarget, final Transfer transfer, final Location location) {
}
}.dropTopAndBottom("tr"));
form.add(dataWrapper);
And the markup:
<div wicket:id="dataWrapper">
<table>
<tbody>
<tr wicket:id="data">
<td wicket:id="label"></td>
</tr>
</tbody>
</table>
</div>
I am using Wicket-DND 0.6.0 and Wicket 6.6.0. When I drag a row using this code, I get the red cross icon displaying in the drag indicator.
wicket-dnd uses standard css selectors to determine drop locations.
The following is the modified TableExample in wicket-dnd-examples:
table.add(new DropTarget(Operation.MOVE)
{
#Override
public void onDrop(AjaxRequestTarget target, Transfer transfer, Location location)
throws Reject
// something was dropped
{
}.dropTopAndBottom("tr");
My problem was caused not by Wicket DND but by a apparently conflicting reference to JQuery UI in my markup (JQuery UI isn't required for Wicket DND, but was present for other components of my application).
Replacing this with a WiQuery-provided reference no longer results in any issues:
#Override
public void renderHead(final IHeaderResponse response) {
super.renderHead(response);
// Ensure that Wicket's jQuery library is always loaded, so we can invoke our own jQuery calls
response.render(JavaScriptReferenceHeaderItem.forReference(getApplication().getJavaScriptLibrarySettings().getJQueryReference()));
response.render(JavaScriptReferenceHeaderItem.forReference(CoreUIJavaScriptResourceReference.get()));
}
in my wicket application I have 3 checkbox in form:
add(new CheckBox("1").setOutputMarkupId(true));
add(new CheckBox("2").setOutputMarkupId(true));
add(new CheckBox("3").setOutputMarkupId(true));
form also contain behavior which unselect checboxes
add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(AjaxRequestTarget target) {
List<Component> components = new ArrayList<Component>();
if (target.getLastFocusedElementId() != null) {
if (target.getLastFocusedElementId().equals("1")) {
components.add(get("2"));
components.add(get("3"));
} else if (target.getLastFocusedElementId().equals("2")) {
components.add(get("1"));
} else if (target.getLastFocusedElementId().equals("3")) {
components.add(get("1"));
}
for (Component component : components) {
component.setDefaultModelObject(null);
target.add(component);
}
}
}
});
this works good on mozilla browser but in chrome this doesnt work. How I can improve to work this on chrome too ?
UPDATE
problem is in:
target.getLastFocusedElementId()
in mozilla this return what I want but in chrome it always return null but I dont know wh
UPDATE 2
google chrome has bug in focus element:
http://code.google.com/p/chromium/issues/detail?id=1383&can=1&q=window.focus%20type%3aBug&colspec=ID%20Stars%20Pri%20Area%20Type%20Status%20Summary%20Modified%20Owner
so I need to do this in other way
Client-sided solution (Javascript)
You could save an Ajax call and gain responsiveness by coding that behavior client-side. In the simplest of the scenarios, if you can give those checkboxes a fixed "id" attribute, you could use Component.setMarkupId() to force an id value for the checks (make sure it is unique in the DOM):
Java
add(new CheckBox("1").setOutputMarkupId(true).setMarkupId("check1");
add(new CheckBox("2").setOutputMarkupId(true).setMarkupId("check2");
add(new CheckBox("3").setOutputMarkupId(true).setMarkupId("check3");
HTML
<input type="checkbox" wicket:id="1" onclick="uncheck(this,1)"/>
<input type="checkbox" wicket:id="2" onclick="uncheck(this,2)"/>
<input type="checkbox" wicket:id="3" onclick="uncheck(this,3)"/>
Javascript
function uncheck(comp, idx){
if (comp.checked) {
if (idx == 1){
document.getElementById("check2").checked = false;
document.getElementById("check3").checked = false;
}
else if (idx == 2 || idx == 3){
document.getElementById("check1").checked = false;
}
}
}
Java only solution (Ajax)
If you really want to do this server-side, it would make sense to also propagate the values server side (instead of relying on which component was last focused), for instance with AjaxFormComponentUpdatingBehavior.
final CheckBox c1 = new CheckBox("on");
final CheckBox c2 = new CheckBox("off");
c1.setOutputMarkupId(true);
c2.setOutputMarkupId(true);
c1.add(new AjaxFormComponentUpdatingBehavior("onclick") {
private static final long serialVersionUID = 1L;
protected void onUpdate(final AjaxRequestTarget target) {
if (Boolean.TRUE.equals(c1.getModelObject())) {
c2.setModelObject(Boolean.FALSE);
target.add(c2);
}
}
});
c2.add(new AjaxFormComponentUpdatingBehavior("onclick") {
private static final long serialVersionUID = 1L;
protected void onUpdate(final AjaxRequestTarget target) {
if (Boolean.TRUE.equals(c2.getModelObject())) {
c1.setModelObject(Boolean.FALSE);
target.add(c1);
}
}
});
add(c1);
add(c2);
If this constraint is important for the integrity of your data model, you should also implement it in a FormValidator so that invalid input never reaches the Form's Models. Users without JS enabled or just fiddling with the DOM could bypass it (the same would apply if using an Ajax Behavior).
OK I fix chrome bug with focus when I add this behavior to checkboxes:
public class DefaultFocusBehavior extends AjaxEventBehavior {
public DefaultFocusBehavior(String event) {
super(event);
}
private static final long serialVersionUID = 1;
private Component component;
#Override
protected void onEvent(AjaxRequestTarget target) {
target.appendJavaScript(("document.getElementById('" + component.getMarkupId() + "').blur();"
+ "document.getElementById('" + component.getMarkupId() + "').focus();"));
}
protected void onBind() {
this.component = getComponent();
component.setOutputMarkupId(true);
}
}
so now my code is working on chrome too
Using tapestry5-jquery and Dialog component, ¿How do I implement a close action for the parent dialog?. I mean a button that executes some code and then close parent dialog without changing of page.
This is the javascript only version of what I'm doing:
<div id="container">
¿Are you sure to delete selected items?
</div>
$('#container').dialog({
modal : true,
buttons:[{
text: "Yes",
click: function() {
//Perform action here, then close dialog.
$(this).dialog("close");
}
},{
text: "No",
click: function() {
//Only close dialog
$(this).dialog("close");
}
}
}]
});
But I need to use Tapestry 5 tags and java class methods:
<t:jquery.dialog t:clientId="delDialog">
¿Are you sure to delete selected items?
<input t:type="submit" t:id="delYes" value="Yes"/>
<input t:type="submit" t:id="delNo" value="No"/>
</t:jquery.dialog>
Java class:
public class UserAdmin {
#OnEvent(component = "delYes", value = EventConstants.SELECTED)
void delYesClicked(){
//Delete selected items
}
#OnEvent(component = "delNo", value = EventConstants.SELECTED)
void delNoClicked(){
//Close dialog
}
}
Thanks.
You could do something like this if the clientId is always the same (i.e. 'delDialog')
#Inject
private AjaxResponseRenderer ajaxResponseRenderer;
protected void addCloseDialogCommand() {
ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
#Override
public void run(JavaScriptSupport javascriptSupport) {
javascriptSupport.addScript("$('#delDialog').dialog('close');");
}
});
}
...and call the method in your event handler:
#OnEvent(component = "delNo", value = EventConstants.SELECTED)
void delNoClicked() {
addCloseDialogCommand();
}
This behavior could be implemented using a mixin that you apply on any element you want.
Suggested mixin:
public class DialogButtonHandler {
#Parameter(value = "dlgId", defaultPrefix = BindingConstants.LITERAL)
private String dlgId;
#Inject
private JavaScriptSupport javaScriptSupport;
#InjectContainer
private ClientElement element;
#AfterRender
public void afterRender() {
javaScriptSupport.addScript(
"$('#%s').click(function(){$('#%s').dialog('close');});",
element.getClientId(), dlgId);
}}
Markup:
<t:jquery.dialog t:clientId="delDialog">
¿Are you sure to delete selected items?
<input t:type="submit" t:id="delYes" value="Yes" />
<input t:type="submit" t:id="delNo" value="No" t:mixins="dialogButtonHandler" t:dlgId="delDialog"/>
</t:jquery.dialog>
I have a Page with a Wizard component. The user can navigate the panels of the wizard by using the next and previous buttons which I have performing full (non-ajax) form submissions so that the app is back-button friendly.
When the next button is clicked, I would like to attempt ajax form validation (if javascript is enabled). I tried doing:
nextButton.add( new AjaxFormValidatingBehavior( form, "onsubmit") );
to add such validation. The behaviour works - however, when validation errors occur the browser still submits the entire form.
What is the Wicket way to prevent the browser from submitting the form in this case?
Override the onError() method on either the form or the AjaxFormValidatingBehavior. If you do it on the behavior, I am not sure if that will prevent the form from submitting or not.
new AjaxFormValidatingBehavior( form, "onsubmit") {
public void onSubmit() {}
public void onError() {}
}
Maybe a bit to late but here is the answer:
public class SomePage extends WebPage {
private FeedbackPanel feedbackMessageError = new FeedbackPanel("feedbackTabAddEmpMesError", new ExactLevelFeedbackMessageFilter(FeedbackMessage.ERROR));
public SomePage(String id) {
final Form<Void> form = new Form<>("tabFormAddEmp");
add(form);
//Name textfield cannot be empty
final FormComponent<String> tabAddEmpName = new RequiredTextField<>("tabAddEmpName", Model.of(""));
tabAddEmpName.setLabel(Model.of("Name"));
tabAddEmpName.setOutputMarkupId(true);
//Salarynumber has to be minimal 10 char long
final FormComponent<String> tabAddEmpLoon = new RequiredTextField<>("tabAddEmpLoon", Model.of(""));
tabAddEmpLoon.add(new StringValidator(10, null)).setLabel(Model.of("Salarynumber"));
tabAddEmpLoon.setOutputMarkupId(true);
final Button button = new Button("tabFormAddEmpBut");
form.add(tabAddEmpName , tabAddEmpLoon, button);
button.add(new AjaxFormValidatingBehavior(form, "onclick") {
#Override
public void onError(AjaxRequestTarget target) {
//Add feedbackpanel to your html and voila!
target.add(feedbackMessageError);
}
#Override
protected void onSubmit(AjaxRequestTarget target) {
//Do some logic over here
}
}
}
}