Tapestry, Get selected item from loop - java

I'm currently working on some project, using Apache Tapestry 5.3.6. I have issue using t:loop component. Is there any way I can get selected item after loop finishes, and page is rendered?
What I need to achieve is: Let's say I have loop like this:
<t:loop t:source="itemList" t:value="item">
<t:actionlink id="something" context="item.ID"></t:actionlink>
</t:loop>
This will work fine. But if I move this actionlink into my own component, and pass this ID through my parameter, if I click, I always get the last item from list, and not the one which is clicked.
<t:loop t:source="itemList" t:value="item">
<t:mycomponent myparameter="item.ID"></t:mycomponent>
</t:loop>
I tried putting formState="iteration", and puting ValueEncoder, but nothing helps.
Please, can anyone help me, and show me how to solve this issue, and get the selected item from the list.
Thanks in advance
Edit: Here is code of my component
public class Ocenjivanje
{
#Parameter(required=true)
#Property
private int materijalID;
private Materijal materijal;
#Inject
private Session session;
#SessionState
private User user;
#CommitAfter
public Object unesiOcenu(int ocena)
{
Materijal m = (Materijal)session.createCriteria(Materijal.class).add(Restrictions.eq("materijalID", this.materijalID)).list().get(0);
Date d = new Date();
Ocena o = new Ocena();
o.setMaterijal(m);
o.setKorisnikID(this.user.getID());
o.setDatumOcene(d);
o.setOcena(ocena);
session.save(o);
return this;
}
public void onActionFromJedan()
{
unesiOcenu(1);
}
public void onActionFromDva()
{
unesiOcenu(2);
}
public void onActionFromTri()
{
unesiOcenu(3);
}
public void onActionFromCetiri()
{
unesiOcenu(4);
}
public void onActionFromPet()
{
unesiOcenu(5);
}
}
<t:container
xmlns="http://www.w3.org/1999/xhtml"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
Oceni sadržaj:
<t:actionlink t:id="jedan">1</t:actionlink>
<t:actionlink t:id="dva">2</t:actionlink>
<t:actionlink t:id="tri">3</t:actionlink>
<t:actionlink t:id="cetiri">4</t:actionlink>
<t:actionlink t:id="pet">5</t:actionlink>

I'm not quite sure what you are trying to achieve but in any case you do not use the context you pass in in your actionlinks and use hardcoded int's in stead. Change your action links to <t:actionlink t:id="tri" context="materijalID">3</t:actionlink> and change your event handlers to
public void onActionFromJedan(int context)
{
unesiOcenu(context);
}

Related

ZK Framework - How to access the onSelect method of a Combobox that is inside a Listbox generated with a render?

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.

How to refresh label after changing color in wicket

I use Wicket 1.5
When i change color it is really changed on the page only after refreshing using F5. How to refresh it in backend?
I use this lines for changing color:
dateDescription.add(AttributeModifier.replace("style", "color:red;"));
add(dateDescription);
UPDATE #1
Now i use AJAX but still have to refresh page for changing color. Could you tell me what i did wrong?
// in page class
public class FilterUpdateBehavior extends AjaxFormComponentUpdatingBehavior {
public FilterUpdateBehavior(String event) {
super(event);
}
#Override
protected void onUpdate(AjaxRequestTarget target) {
RefreshResult result = getResult(target);
if (result.getStatus() == RefreshResultStatus.DATE_NOT_SET) {
dateIntervalFilterPanel.setAlarmDateStatus(true);
} else {
dateIntervalFilterPanel.setAlarmDateStatus(false);
}
}
}
// in date panel class
dateDescription.add(new AttributeModifier("style", new AbstractReadOnlyModel<String>() {
private static final long serialVersionUID = 1L;
#Override
public String getObject() {
String cssClass = null;
if (isAlarmDateStatus()) {
cssClass = "color:red;";
} else {
cssClass = "color:black;";
}
return cssClass;
}
}));
add(dateDescription);
UPDATE #2
public RefreshResult getResults(AjaxRequestTarget target) {
// ... somewhere here additional logic of getting particulate RefreshResult
target.add(table);
target.add(paging);
target.add(loadingPanel);
return new RefreshResult(resultType);
}
UPDATE #3 FINAL (IT HELPED ME)
I miss this code line when i change isAlarmDateStatus, now it works fine. Thanks to Andrea!
target.add(dateDescription);
your code line looks right but you must use AJAX to reflect your changes without reloading the entire page. Unfortunately Wicket 1.5 is really outdated and there are few resources online to provide you an example of AJAX support. You might try to look into the old 1.5 AJAX examples code here:
https://github.com/apache/wicket/tree/build/wicket-1.5.17/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin

How can I use jQuery in a custom Tapestry 5 component

I am attempting to create a component that if given the following tml:
<t:slideout>
<p:header>Short Description of Data</p:header>
Long Details about the data here
</t:slideout>
This should initially render the block in the header parameter, when this block is clicked I want the long details section to slide out using the jQuery .slideDown() function or equivalent.
Currently I have the following class:
public class slideout
{
#Parameter(name="header", defaultPrefix = BindingConstants.LITERAL)
private Block headerBlock;
public Block getHeader()
{
return headerBlock;
}
}
and the corresponding slideout.tml file:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
<body>
<t:delegate to="header"/>
<t:body/>
</body>
</html>
We are already making use of the tapestry5-jQuery library, this component needs be able to be used multiple times on the same page so I'm also not sure of how to ensure that there are no ID collisions when rendering the page.
I'm not sure where to progress from here, if I was doing this in raw HTML/jQuery I'd do something like
$('slideout-header').click(function() {
$(this).next('slideout-body').slideDown();
});
However I'm not sure of what the "Tapestry" way of constructing these classes would be. What is the best way to solve this problem in Tapestry 5?
You can add a Slideout.js file next to your Slideout.tml:
Tapestry.Initializer.Slideout = function (parameters) {
var yourClientId = parameters.clientId;
//your javascript init script here
};
Add to your Slideout.java:
#Import(library = {"Slidout.js"})
public class Slideout {
#Inject
private JavaScriptSupport javaScriptSupport;
#Parameter(name="header", defaultPrefix = BindingConstants.LITERAL)
private Block headerBlock;
#Property
#Parameter(value = "prop:componentResources.id", defaultPrefix = "literal")
private String clientId;
#AfterRender
private void afterRender() {
JSONObject props = new JSONObject();
props.put("clientId", clientId);
javaScriptSupport.addInitializerCall("Slideout", props);
}
public Block getHeader()
{
return headerBlock;
}
}
and your Slideout.tml (note that I removed the html so that you can use Slideout as a component)
<div id="${clientId}" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
<t:delegate to="header"/>
<t:body/>
</div>
Disclaimer: I have not tested this code so have a play.

Hiding a component in Wicket

I have a component inside an <a/> tag that opens a popup window on click. It's an "add to favourite" link which works on KML files. My KML file has a field named "favourite[boolean]". Now I'd like to hide or show my "add to favourite" link. The KML list is generated with a table:
public class CustomTracksAjaxDataTable<T> extends CustomAjaxDataTable<T> {
public CustomTracksAjaxDataTable(String id, List<IColumn<T>> iColumns,
ISortableDataProvider<T> tiSortableDataProvider, int rowsPerPage) {
super(id, iColumns, tiSortableDataProvider, rowsPerPage);
}
protected void onEventHandler(AjaxRequestTarget ajaxRequestTarget,
KMLFile file) {
setKMLData(file); // it just update map, dont care about it
add(new FavouriteStarIconState(file.isSaved()));
}
}
I tried to add a behavior thus:
public class FavouriteStarIconState extends AbstractDefaultAjaxBehavior {
private boolean isFavourite;
public FavouriteStarIconState(boolean isFavourite) {
super();
this.isFavourite = isFavourite;
}
#Override
protected void respond(AjaxRequestTarget target) {
if (isFavourite) {
target.appendJavascript("jQuery('.map_container_star').css(
{'display' : 'none' });");
} else {
target.appendJavascript("jQuery('.map_container_star').css(
{'display' : 'block' });");
}
}
#Override
public void renderHead(IHeaderResponse response) {
response.renderOnLoadJavascript(getCallbackScript().toString());
}
}
The part of the HTML containing the component:
<div id="map_container">
<a wicket:id="favourite_star" class="map_container_star"></a>
</div>
This isn't working. I got the same result with component.setVisible(false). How can I get hiding to work?
Well it finds out that I make a terrible mistake and put javascript appending in wrong place. AJAX request was not rendered. The proper class was CustomAjaxDataTable being extended by my class CustomTracksAjaxDataTable. I just add
new AjaxEventBehavior( "onclick" )
and override
protected void onEvent( AjaxRequestTarget ajaxRequestTarget )
and it works great now
You could use a CSS class like this
.hiddenClass
{
visibility:hidden;
}
then with AttributeModifier you add the class to the element
component.add(new AttributeModifier("class", "hiddenClass"));
or add the style directly to the style attribute
component.add(new AttributeModifier("style", "visibility:hidden;"));

How to disable / change style of wicket button link in onClick()

In a Wicket app, I have a bunch of <button> elements to which I'm attacking a Link component. Now in the onClick() method of the component I want to disable or change the style of the button. How can I do that? Calling setEnabled(false) has no effect.
Repeated uses of onClick() are operating on the same object in memory. If you're not using Ajax, you can still maintain some state in an anonymous subclass of Link. Then, you can use onBeforeRender() and onComponentTag() to change how it is displayed each time.
Link<Void> link = new Link<Void>("myLink") {
private String customCSS = null;
private boolean customEnabled = true;
public void onClick() {
if (/* test to determine disabled */) {
customCSS = "disabled";
customEnabled = false;
} else {
customCSS = null;
customEnabled = true;
}
}
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
if (customCSS != null)
tag.put("class", customCSS);
}
#Override
public boolean isEnabled() {
return super.isEnabled() && customEnabled;
}
};
AttributeModifiers (or other behaviors) aren't good for this case because, if you add them in the onClick() method, they will begin stacking on the same link for each click - since they are maintained as part of the Link's state.
Your Link can keep track of all manner of state, allowing your onClick() method to enable/disable/change/etc with repeated clicks.
You can also override onBeforeRender(), isVisible(), and other methods that are run each time the link is displayed on the page. The constructor, onConfigure(), and others are run just once, regardless of how many times you click the button.
I don't think this is an entirely good idea in Wicket. Of course it could be done by trickery, but it's far simpler to either:
Override the isEnabled() method to return a value derived from the model of the form/component.
Attach an AttributeModifier when you create the component, and use a model for it which returns a value derived as above.
Whichever you choose, the principle is to let Wicket "pull" rendering information in rather than pushing it explicitly.
The answer provided by Michael Borgwardt is nearly correct.
The problem is that you use Link. Disabled Links use <span> instead of
<a>/<button> and are surrounded with <em> by default. Using Button
component will set 'disabled' attribute in the element.
I would like to add, that you need to use HTML button element instead of <a> (link). Original answer can be counfusing, because Link and Button also exist in Wicket.
I think AjaxCallDecorator should be the class you need to use to disable/change style of the button.
The problem is that you use Link. Disabled Links use <span> instead of <a>/<button> and are surrounded with <em> by default.
Using Button component will set 'disabled' attribute in the element.
Take a look at SimpleAttributeModifier and AttributeAppender. Depending on your actual requirements one of those should do the trick. SimpleAttributeModifier adds or replaces an attribute of any HTML-Tag that has a prepresentation in wicket (replaces the css class), while AttributeAppender appends to the attributes (adds another css class). This should work for enabling/disabling buttons as well but I haven't tried that.
Example:
Label label = new Label("id", "Some silly text.")
add(label);
label.add(new SimpleAttributeModifier("class", "my-css-class");
For Ajax you'll have to add the component to the target as well.
More detailed example:
Java code:
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.model.Model;
public class DemoPage extends WebPage {
public DemoPage() {
Form form = new Form("form");
add(form);
final WebMarkupContainer wmc = new WebMarkupContainer("greenText");
form.add(wmc);
form.add(new Link("redLink"){
#Override
public void onClick() {
wmc.add(new SimpleAttributeModifier("class", "redText"));
}});
final Button boldButton = new Button("boldButton"){
#Override
public void onSubmit() {
wmc.add(new AttributeAppender("class", true, new Model<String>("boldText"), " "));
}};
form.add(boldButton);
Link disabler = new Link("buttonDisabler") {
#Override
public void onClick() {
boldButton.add(new AttributeAppender("disabled", true, new Model<String>("disabled"), " "));
}
};
form.add(disabler);
}
}
corresponding HTML:
<html>
<head>
<style>
.redText {
color: red;
}
.greenText {
color: green;
}
.boldText {
font-weight: bold;
}
</style>
</head>
<body>
<form wicket:id="form">
<div class="greenText" wicket:id="greenText">This is Green.</div><br />
Make it red<br />
<input type="submit" wicket:id="boldButton" value="Make it bold" /><br />
Disable the button
</form>
</body>
</html>

Categories