html file which looks like this:
<wicket:panel>
<form wicket:id="adminUIForm">
<table wicket:id="datatable" class="datatable"></table>
<div wicket:id="institutTablePanel"></div>
</form>
</wicket:panel>
But in my Java Code I only want to instantiate the component institutTablePanel, when a row in the datatable is clicked, because the constructor looks like this:
target.add(new InstitutTablePanel("institutLabelPabel", selectedContact));
And selectedContact cannot be empty.
Is there a possibility of doing this without getting an error that it cannot find the component in my JavaCode?
Edit:
This might be helpful
#Override
protected Item<Kontakt> newRowItem(final String id, final int index,
final IModel<Kontakt> model) {
Item<Kontakt> rowItem = new Item<Kontakt>(id, index, model);
rowItem.add(new AjaxEventBehavior("onclick") {
private static final long serialVersionUID = 1L;
#Override
protected void onEvent(AjaxRequestTarget target) {
selectedKontakt = model.getObject();
target.add(new InstitutTablePanel("institutTablePanel", selectedKontakt));
}
});
return rowItem;
}
Create an empty WebMarkupContainer that is a blank div and add it to your page/parent component. Next, onClick, use addOrReplace and replace the the WebMarkupContainer with the InstitutTablePanel. Both the WebMarkupContainer and the InstitutTablePanel should have the same wicket:id
You have to target existing components in your ajax calls, therefore i recommend initializing an InstitutTablePanel on initialization of the page with the model as Panel model. Passing model objects without the wrapping model is discouraged in wicket and leads to unnecessary problems.
You can change the InstitutTablePanel to be empty when its model object is null or empty.
Also, Wicket is built around passing models to components and not plain objects which might seem unnecessary at first but when complexity rises the pattern is preventing many bugs and errors.
Edit: dont forget to use setoutputmarkupid(true) on the added panel.
Related
Good evening, I tell you my problem:
In the ZK Framework I need to use the onSelect method of a dynamically rendered Combobox within a Listbox that is also rendered.
When I select one of the Combobox options, its content should be saved in the observaciones variable of the DocumentoVinculado class. But the onSelect don't work! I appreciate any help. Attached code:
.zul
<zk>
<window id="myWindow" apply="com.curso.controller.NewFileComposer" title="Help">
<listbox id="myListbox">
<listhead>
<listheader label="NroGEBI"></listheader>
<listheader label="Observaciones"></listheader>
</listhead>
</listbox>
<label id="myLabel"></label>
</window>
</zk>
Composer / Controller
public class NewFileComposer extends BaseController {
private Window myWindow;
private Listbox myListbox;
private Combobox myCombobox0;
private Combobox myCombobox1;
private Label myLabel;
public void onSelect$myCombobox0() { myLabel.setValue(myCombobox0.getValue()); }
public void onSelect$myCombobox1() { myLabel.setValue(myCombobox1.getValue()); }
public void onCreate$myWindow() {
ListModelList<DocumentoVinculado> modelo = new ListModelList<>(crearLista());
myListbox.setModel(modelo);
myListbox.setItemRenderer(new NewFileRender());
}
private List<DocumentoVinculado> crearLista() {
List<DocumentoVinculado> docVinculados = new ArrayList<>();
docVinculados.add(new DocumentoVinculado("123GEBI1", " "));
docVinculados.add(new DocumentoVinculado("123GEBI2", " "));
return docVinculados;
}
}
Render
public class NewFileRender implements ListitemRenderer {
#Override
public void render(Listitem item, Object data, int i) throws Exception {
DocumentoVinculado docVinculado = (DocumentoVinculado) data;
Listcell nroGebiCell = new Listcell(docVinculado.getNroGEBI());
nroGebiCell.setParent(item);
Listcell opcionesCell = new Listcell();
opcionesCell.appendChild(comboboxObservaciones(i));
item.appendChild(opcionesCell);
}
private Combobox comboboxObservaciones(int i) {
Combobox combobox = new Combobox();
List<String> listaDeOpciones = listaDeOpciones();
for(String opcion : listaDeOpciones) {
Comboitem myComboitem = new Comboitem();
myComboitem.setLabel(opcion);
myComboitem.setParent(combobox);
}
combobox.setId("myCombobox" + i);
return combobox;
}
private List<String> listaDeOpciones() {
List<String> opciones = new ArrayList<>();
opciones.add(" ");
opciones.add("Opcion1");
opciones.add("Opcion2");
return opciones;
}
}
Thank you for reading. Cheers!
From your syntax, it looks like you are using GenericForwardComposer as the super class for your BaseController.
Is that correct?
Depending on how much you have already done, I'd recommend checking if you could switch to SelectorComposer instead.
They work the same way (wire components, and listen to events), but the SelectorComposer is much more explicit in how things are wired. GenericForwardComposer can be somewhat error-prone unless you are very clear about its lifecycle.
Regarding why the onSelect would not be firing in this case:
ZK Composers works in phases. One important phase is the "compose" phase, during which the components are created, events listeners are put in places, etc.
At the end of that phase, the "afterCompose" phase takes place. During afterCompose, "magic" stuff (like the wiring of the private components in your current composer, or the auto-forwarding of the onSelect$myCombobox0 in the same class) takes place. It will also attempt to rewire stuff that was missing again just before triggering onCreate on its root anchor component.
Now, if your comboboxes are created dynamically after that step (for example, during an "onCreate$myWindow" event which happens after all of the above is finished), the composer will have already done all of the wiring and forwarding, and will not know to re-check the components for additional wiring.
With all of that explained, what can you do about it?
First, you can consider moving the onCreate code to a doAfterCompose method.
Instead of waiting for onCreate to generate the Listbox content, if you do it during doAfterCompose (override from genericFowardComposer), you should be early enough that auto-wiring will trigger on these components.
That should look like this:
#Override
public void doAfterCompose(Component comp) {
ListModelList<DocumentoVinculado> modelo = new ListModelList<>(crearLista());
myListbox.setModel(modelo);
myListbox.setItemRenderer(new NewFileRender());
}
2nd, if you move from genericForwardComposer to SelectorComposer, you can actually tell the composer to rewire itself "on demand" by using the Selectors.wireComponents method. Of course, that's only valid if your application can be refactored for this change.
I have the following java and html code:
this.leakageModel = new PropertyListView<Leakage> ( "leakage", new ArrayList<Leakage> ()) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem (final ListItem<Leakage> item) {
Link<String> brandLink = new Link<String> ("brandLink") {
private static final long serialVersionUID = -480222850475280108L;
#Override
public void onClick () {
//change another model in the page to update
//another table when the link is clicked
}
};
brandLink.add (new Label ("brand"));
item.add (brandLink);
} };
add (this.leakageModel);
html file:
<tr wicket:id="leakage" class="testClass">
<td class="testClass">
<a wicket:id="brandLink" href="#">
<span wicket:id="brand"></span>
</a>
</td>
</tr>
What I want to do is to be able to call a javascript function from inside the onClick() method.
The model update that I currently do inside the onClick method works well and updates another table on the page.
However everything I have tried to call a javascript function or change the css style has failed.
For instance:
Adding a css class:
add (new AttributeAppender("class", new Model("anotherclass"), " "));
Using an AjaxLink type instead, and a number of other things I have tried to no avail.
On a related note, my original intention is to hide all rows in the table except the one I have clicked. Maybe I can do this just from the Java code and have no need for Javascript at all, but updating the css as above doesn't work.
Any suggestions as to what am I doing wrong?
On a related note, my original intention is to hide all rows in the
table except the one I have clicked.
Instead of answering your question, I will try to provide a solution to your problem :).
It makes perfect sense to hide the table row via javascript. I would suggest doing it with Jquery as described in Hiding all but first table row with jQuery:
$("#myTbl tr:not(nth-child(3))").hide();
Now, you have to execute the above javascript snippet each time a user clicks your Wicket link. For this, you can for example create your own link class like this:
public class JavascriptLink extends Label{
public JavascriptLink(String id, String label) {
super(id, label);
add(new AttributeAppender("onclick", "...your javascript here..."));
}
}
I leave it to you to combine the jquery with the JavascriptLink to meet your requirements. It should work going in this direction.
I have the following ui situation:
<g:DisclosurePanel width="100%" ui:field="disclosurePanel">
<g:customHeader>
<g:HorizontalPanel width="100%" ui:field="tableRow">
<g:cell width="16px" horizontalAlignment="ALIGN_CENTER">
<g:Image url="images/plus-icon.gif" ui:field="icon"></g:Image>
</g:cell>
<g:cell width="20%">
<g:Label ui:field="productName"></g:Label>
</g:cell>
<g:cell>
<g:Anchor ui:field="info"><ui:msg>More info...</ui:msg></g:Anchor>
</g:cell>
</g:HorizontalPanel>
</g:customHeader>
<g:VerticalPanel width="100%" ui:field="details">
<!-- details panel here -->
</g:VerticalPanel>
</g:DisclosurePanel>
And I would like to bind an event handler method to the Anchor info. However every widget I have in the header opens and closes the disclosurepanel, even if I hook something on the info by:
#UiHandler("info")
public void onInfoClicked(ClickEvent event)
{
// do something custom, but do not open/close the disclosurepanel
}
I hope that this can be achieved without making a custom composite or stuff. Can you help me?
DisclosurePanel's header is private inner class ClickableHeader:
private final class ClickableHeader extends SimplePanel {
private ClickableHeader() {
// Anchor is used to allow keyboard access.
super(DOM.createAnchor());
Element elem = getElement();
DOM.setElementProperty(elem, "href", "javascript:void(0);");
// Avoids layout problems from having blocks in inlines.
DOM.setStyleAttribute(elem, "display", "block");
sinkEvents(Event.ONCLICK);
setStyleName(STYLENAME_HEADER);
}
#Override
public void onBrowserEvent(Event event) {
// no need to call super.
switch (DOM.eventGetType(event)) {
case Event.ONCLICK:
// Prevent link default action.
DOM.eventPreventDefault(event);
setOpen(!isOpen);
}
}
}
assuming your code:
#UiField
DisclosurePanel p;
//call this somewhere once on widget creation to
//prevent header's default click handler
private void myInit()
{
p.getHeader().unsinkEvents(Event.ONCLICK);
}
#UiHandler("info")
public void onInfoClicked(ClickEvent event)
{
//trigger "standard" click handler if you want
if(someCondition) {
//convert GwtEvent descendant to NativeEvent descendant;
p.getHeader().onBrowserEvent(event.getNativeEvent().<Event> cast());
}
// do something custom, but do not open/close the disclosurepanel
}
Well it works as it is designed. Each element you put inside the DisclosurePanel has a click handler which opens/ closes it. So inside your header there should be only images and or text, basically onyl elements with no atteched logic. I would consider arranging your html elements different, so the link isn't inside the header...
If you really, really have to put it inside the header, you can add this to your Anchor click event:
disclosurePanel.setOpen(!disclosurePanel.isOpen());
This will restore the previous state of the DisclosurePanel. The good part is, that it is so fast, that you don't even see the DisclosurePanel opening/closing, the bad part is, that this is really bad design....
Addition:
the DisclosurePanel uses Anchors to be displayed. An anchor allowes per definition only block elements, so you shouldn't use it like this at all! (See Is putting a div inside a anchor ever correct?)
I'm trying to figure out how to make a dynamically generated csv available to a dygraphs JavaScript.
I'm using a wicket behavior to add the dygraph (JavaScript graph) to my markup like shown in the codesample bellow. Right now I've hardcoded it to use a csv file named "dygraph.csv". I want to change this, and instead make dygraph use the values from String csv, how do I achieve this?
Any help help is greatly appreciated.
public class DygraphBehavior extends AbstractBehavior {
private static final long serialVersionUID = -516501274090062937L;
private static final CompressedResourceReference DYGRAPH_JS = new CompressedResourceReference(DygraphBehavior.class, "dygraph-combined.js");
#Override
public void renderHead(IHeaderResponse response) {
response.renderJavascriptReference(DYGRAPH_JS);
}
#Override
public void onRendered(Component component) {
final String id = component.getId();
Response response = component.getResponse();
response.write(JavascriptUtils.SCRIPT_OPEN_TAG);
response.write("new Dygraph(document.getElementById(\""+id+"\"), \"dygraph.csv\", {rollPeriod: 7, showRoller: true, errorBars: true});");
response.write(JavascriptUtils.SCRIPT_CLOSE_TAG);
}
}
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
add(new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes())));
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
<div>
<h1>Dygraph:</h1>
<div wicket:id="graphdiv" id="graphdiv" style="width:500px; height:300px;"></div>
<a wicket:id="csv" href="#">dl generated csv</a>
</div>
public class Dygraph extends WebPage {
public Dygraph() {
String csv = "Date,ms\n20070101,62\n20070102,62";
ResourceLink<File> link = new ResourceLink<File>("csv", new ByteArrayResource("text/csv", csv.getBytes()));
add( link );
//this is the url that should be passed to the javascript code
CharSequence url = link.urlFor( IResourceListener.INTERFACE );
add(new Label("graphdiv").add(new DygraphBehavior()));
}
}
There are other solutions based on the scope of your resource, maybe a dynamic shared resource would work better (if your graph parameters can simply be passed as url parameters), but this will work.
The JavaScript needs to see the data in some way after the page has been rendered. So you have two options:
Embed the data in the page (say in a hidden div) and then let JavaScript read the data from there as text.
Create a servlet where the JavaScript can download the data from.
The second option means that your page rendering code has to pass the data somehow to the servlet. You can try to put it into the session but then, it will sit there, occupying RAM. Probably not a problem if it's just a little bit of data and you have only a few users. But if that's not true, option #1 is probably better.
What's the cleanest way I can make a checkbox automatically submit the form it belongs to in Wicket? I don't want to include a submit button at all. The checkbox is backed by a boolean field in a domain object ("Account" in this case).
Simplified example with irrelevant parts omitted:
EntityModel<Account> accModel = new EntityModel<Account>(Account.class, id);
PropertyModel<Boolean> model = new PropertyModel<Boolean>(accModel, "enabled");
CheckBox checkBox = new CheckBox("cb", model);
Form form = new Form("form");
form.add(checkBox);
add(form);
HTML:
<form wicket:id="form" id="form" action="">
<input wicket:id="cb" type="checkbox" />
</form>
Edit: To clarify, my goal is just to change the domain object's field (-> value in database too) when the checkbox is toggled. Any (clean, easy) way to achieve that would be fine. (I'm not sure if you actually need the form for this.)
Just overriding wantOnSelectionChangedNotifications() for the checkbox—even without overriding onSelectionChanged()—seems to do what I want.
This way you don't need the form on Java side, so the above code would become:
EntityModel<Account> accModel = new EntityModel<Account>(Account.class, id);
add(new CheckBox("cb", new PropertyModel<Boolean>(accModel, "enabled")){
protected boolean wantOnSelectionChangedNotifications() {
return true;
}
});
Feel free to add better solutions, or a better explanation of what's going on with this approach!
Edit: On closer inspection, I guess the method's Javadoc makes it reasonably clear why this does what I wanted (emphasis mine):
If true, a
roundtrip will be generated with each
selection change, resulting in the
model being updated (of just this
component) and onSelectionChanged
being called.
While this may work, you are far better off using AjaxCheckBox. An anonymous subclass can be wired to receive events immediately as well as make changes to the UI outside the checkbox itself.
final WebMarkupContainer wmc = new WebMarkupContainer("wmc");
final EntityModel<Account> accModel = new EntityModel<Account>(Account.class, id);
wmc.setVisible(false);
wmc.setOutputMarkupPlaceholderTag(true);
form.add(new AjaxCheckBox("cb", new PropertyModel<Boolean>(accModel, "enabled")) {
#Override
protected void onUpdate(AjaxRequestTarget target) {
wmc.setVisible(accModel.isEnabled());
target.addComponent(wmc);
// .. more code to write the entity
}
});
In this contrived example, the WebMarkupContainer would be made visible in sync with the value of the checkbox.