I am understanding how to handle JS events like onfocus and onblur in apache wicket. I have a simple form in which there is a textfield. "onfocus" event on this I am trying to set the textfield to a value. I have observed on running the code that onfocus is called again and again (recursively it seems). I fail to understand why and what I have done wrong.
Below is the code :
HTML:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<head>
<title>Wicket Examples - component reference</title>
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<form wicket:id="form">
<INPUT wicket:id="input" type="text" name="input" style="WIDTH: 800px" />
</form>
Java:
package com.poc.pages;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.Model;
public class IndexPage extends WebPage
{
/**
* Constructor
*/
public IndexPage()
{
Form form = new Form("form");
TextField<String> TextInput = new TextField<String>("input",Model.of(""));
TextInput.add(new AjaxFormComponentUpdatingBehavior("onfocus"){
#Override
protected void onUpdate(AjaxRequestTarget target) {
String thisValue =
this.getComponent().getDefaultModelObjectAsString();
thisValue = "ChangedNormally";
this.getComponent().setDefaultModelObject("ChangedViaDefaultSetModel");
target.add(this.getComponent());
System.out.println("onfocus"+thisValue);
}
});
form.add(TextInput);
add(form);
}
}
When I focus on textfield here, ChangedViaDefaultSetModel is set and on console onfocusChangedNormally gets print continuously. I fail to undertsand few things :
Why does onfocus gets called again and again printing onfocusChangedNormally evertytime on console?
How can I get the value of actual model and not default model.
WHy does the normal value doesnot get reflected in model whereas on doing setDefaultModel() it works?
Thanks For Help
1) This behavior, you implmented, is a little tricky. When you focus your TextField and add this component to the target, then AjaxRequest is updating it. When update is over, component must return its state, thats why focus gained and your behavior method onUpdate is called again.
To resolve this problem you must use some kind of blocking. The easiest way is to use boolean field and check if focus was already gained to prevent another update. (see the code below).
2) Second and third question is about models, and this is very large subject to discuss. You can learn more by reading this and this articles, or "Wicket in Action" and "Wicket cookbook" books.
In my solution I used PropertyModel, that reflects the value of the field you set as a model of the Component. This code:new PropertyModel<String>(this, "modelValue") means that Wicket must search field with name "modelValue" in this (IndexPage) object and set as the model of TextField. That's why now you can only change this object field to set another value to TextField (don't forget to update component after that).
Comments for code:
I have used AjaxEventBehavior but it's ok to use AjaxFormComponentUpdatingBehavior.
I have added onblur behavior to return old value of the TextField just to show possibilities of this solution.
I have changed TextInput to textInput because code conventions said so.
package com.poc.pages;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.PropertyModel;
public class IndexPage extends WebPage {
private String modelValue = "ChangedNormally";
private boolean focusGained = false;
public IndexPage() {
Form form = new Form("form");
TextField<String> textInput = new TextField<String>("input", new PropertyModel<String>(this, "modelValue"));
textInput.add(new AjaxEventBehavior( "onfocus" ) {
#Override
protected void onEvent(AjaxRequestTarget target) {
if (!focusGained) {
modelValue = "ChangedViaDefaultSetModel";
target.add(this.getComponent());
focusGained = true;
System.out.println( "focus gained " + getComponent().getDefaultModelObject() );
}
}
});
textInput.add(new AjaxEventBehavior( "onblur" ) {
#Override
protected void onEvent(AjaxRequestTarget target) {
modelValue = "ChangedNormally";
target.add(this.getComponent());
focusGained = false;
System.out.println( "focus lost " + getComponent().getDefaultModelObject() );
}
});
form.add(textInput);
add(form);
}
}
Hope this helps.
Related
Question relates to Wicket 1.6
I have a wizard step, which includes a Textfield component. When I press the Enter key, this is being handled by the default button of the Wizard bar ('Next'), and it advances to the next step in the Wizard. I don't want this to happen. When I hit Enter on the Textfield I just want the value to be updated, but remain on the same page.
I tried overriding the onBeforeRender() method of my Wizard class, which as you can see sets the default button of the containing form to null. However this now results in the 'Prev' button being triggered when I hit Enter, so the wizard goes back to the previous step.
public class ConfigurationWizard extends Wizard {
....
#Override
protected void onBeforeRender()
{
super.onBeforeRender();
Component buttonBar = getForm().get(BUTTONS_ID);
if (buttonBar instanceof IDefaultButtonProvider)
{
getForm().setDefaultButton(null);
}
}
}
So the basic question is, how do I disable the default button behaviour of the Wizard?
My approach (with a nice Wicket behavior)
Usage
TextField<String> myField = new TextField<String>("myField", myModel());
myField.add(new PreventSubmitOnEnterBehavior());
Behavior
public class PreventSubmitOnEnterBehavior extends Behavior
{
private static final long serialVersionUID = 1496517082650792177L;
public PreventSubmitOnEnterBehavior()
{
}
#Override
public void bind( Component component )
{
super.bind( component );
component.add( AttributeModifier.replace( "onkeydown", Model.of( "if(event.keyCode == 13) {event.preventDefault();}" ) ) );
}
}
This has nothing to do with the wizard buttons.
The TextField <input> is doing a form submit when the Enter key is pressed. This is standard behaviour for the <input> element.
The solution is to catch the Enter key press for the <input> and prevent the default behaviour
This bit of javascript magic does the trick for me:
<script type="text/javascript">
$(document).ready(function() {
$("#gridDiv").delegate("input","keypress",function(e){
if(e.originalEvent.keyCode == 13){
e.preventDefault();
}
});
});
</script>
where 'gridDiv' is the id of the <div> containing the TextField
I prefer another approach:
I use AjaxButtons for every button needed, with the specific submit code in the overrided onSubmit():
AjaxButton linkSubmit = new AjaxButton("linkSubmit")
#Override
public void onSubmit(AjaxRequestTarget target, Form form) {
super.onSubmit();
// Submit code goes here....
// ...
setResponsePage(new NewPage());
}
#Override
public void onError(AjaxRequestTarget target, Form form) {
}
};
My form doesn't need a "onSubmit()" method.
And the markup doesn't have any submit buttons. All buttons are coded like this:
With this approach you don't need to mess with javascript codes. The page simply will do nothing if you press Enter. You'll have to click your buttons to submit each one.
Hope this can help you.
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 will want to use Data binding in Java Class rather than
#bind
With each ListCell in Listbox.
I tried with this example
My ZUl File...
<zk>
<window border="normal" title="hello" apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('com.test.binding.TestRenderer')" >
<button label="ClickMe" id="retrieve"
onClick="#command('onOK')">
</button>
<div height="800px">
<listbox model="#load(vm.model)" itemRenderer="#load(vm.itemRenderer)" vflex="true" multiple="true"/>
</div>
</window>
</zk>
My Java Class or ViewController.....
package com.test.binding;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
import org.zkoss.zk.ui.Component;
import org.zkoss.zkplus.databind.AnnotateDataBinder;
import org.zkoss.zkplus.databind.Binding;
import org.zkoss.zkplus.databind.BindingListModelList;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Textbox;
public class TestRenderer {
ListModelList model = new ListModelList();
private AnnotateDataBinder binder;
#AfterCompose
public void afterCompose(#ContextParam(ContextType.VIEW) Component view) {
binder = new AnnotateDataBinder(view);
List persons = new ArrayList();
model.add(new Person("David", "Coverdale"));
model.add(new Person("Doug", "Aldrich"));
model.add(new Person("Reb", "Beach"));
model.add(new Person("Michael", "Devin"));
model.add(new Person("Brian", "Tichy"));
binder.loadAll();
}
public void setModel(ListModelList model) {
this.model = model;
}
public ListModel getModel() {
return model;
}
// method for ZK 6
public ListitemRenderer getItemRenderer() {
ListitemRenderer _rowRenderer = null;
if (_rowRenderer == null) {
_rowRenderer = new ListitemRenderer() {
public void render(final Listitem item, Object object,
int index) throws Exception {
final Person dataBean = (Person) object;
binder.bindBean(item.getId() , dataBean);
Listcell cell = new Listcell();
Textbox name = new Textbox();
name.setValue(dataBean.getFirstName());
System.out.println(item.getId()+ "------------------>"+item.getId() + ".name");
//binder.addBinding(name, "value", item.getId()+i + ".name", null, null, "both", null, null, null, null);
//binder.addBinding(name, "value",item.getId() + ".name", new String[] {}, "none", "both", null);
cell.appendChild(name);
//cell.addAnnotation(cell, "bind", null);
cell.setParent(item);
}
};
binder.saveAll();
binder.loadAll();
}
return _rowRenderer;
}
#Command
public void onOK() {
binder.saveAll(); //load Inputfields from Form, Constraints will be performed
binder.loadAll();
Collection<Binding> test = binder.getAllBindings();
System.out.println(model);
}
public class Person {
private String firstName;
private String lastName;
public Person(String fn, String ln) {
setFirstName(fn);
setLastName(ln);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String fn) {
firstName = fn;
}
public String getLastName() {
return lastName;
}
public void setLastName(String ln) {
lastName = ln;
}
}
#Command
public void clickMe(){
BindingListModelList blml = (BindingListModelList) getModel();
for (Object object : blml) {
System.out.println(Integer.parseInt((String) object));
}
}
}
Can any one give me the Demo Example How Binding should work with
getItemRendered()
In Listbox
Thanks
You are trying to mix different paradigms. You want to use databinding especially MVVM style but also introduce your custom renderer in the process. Even if this works I think it is a VERY bad practice. Either go pure MVVM databinding where you separate your view from model and only define binding in your view or go with pure MVC and use your own renderer to render your model data anyway you want.
The whole point of databinding is to let binder take care of rendering/updating component state based on databinding annotations.
Talking specifically about your sample code here you are using org.zkoss.bind.BindComposer which will init a binder instance automatically and after that you are also explicitly instantiating a separate AnnotateDataBinder in #AfterCompose so there will be conflict between these two.
My suggestion would be to go with pure MVVM databinding by introdcing <template> in your view and let MVVM binder to render this template using #Bind or #Load annotations OR go with pure Java style MVVM databinding as described in this "MVVM in Java" article
In my opinion, using ListitemRenderer with MVVM is not so bad, sometimes it can be the 'bridge' between zul page and ViewModel, and it can be considered as a part of component (since once you assign a model, listbox will use default renderer to render item if you do not assign a template or a custom renderer) (see List Model). If there is nothing bad that assign only model and render items by default renderer, there is nothing bad assign both model and custom renderer to render items.
Let's define what the 'good' is at first:
-- The zul file do not need to know how ViewModel works, just ask for data and trigger command as needed.
-- The ViewModel do not need to know anything in zul page, just provide data and process some predefined event.
Now thinking about a simple scenario like the one described in the official document:
Performing Actions on Events
see this line
onClick="#command('deleteOrder', cartItem=cartItem)"
and this line
public void deleteOrder(#BindingParam("cartItem") CartItem cartItem)
In this case, the zul page need to know bring the param to deleteOrder command while event triggered, and even have to know the param should assign the variable named cartItem in ViewModel. On the other hand, the ViewModel need to retrieve the param passed from zul page.
This is obviously not the 'good' situation.
With the ListitemRenderer, let's say ShoppingcartOrderlistItemRenderer, we can simply post an event to Listbox (e.g., onDeleteOrder) with the required data, then make it becomes
<listbox onDeleteOrder="#command('deleteOrder')" ...
and
public void deleteOrder(#ContextParam(ContextType.TRIGGER_EVENT) DeleteOrderEvent event) {
getShoppingCart().remove(event.getProductId());
}
Rely on an event instead of rely on a param defined in zul page, I think this is more robust.
Finally, still have to say, just personal opinion.
#subodh
You can also try add EventListener in ListitemRenderer and pass some custom event with required data to listbox then bind that custom event to ViewModel, please refer to
the sample project at github and the demo online
Edit:
A related post at my blog: ZK Listbox: Event Processing with Renderer and MVVM
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>
I am creating a mixin which renders a javascript file when a textfield gains focus.
I am new to the idea of mixins in Tapestry, and I am unsure of where to place my original javascript file which i wish to run when the textfield gains focus.
The following is an example of my code:
The Java mixin class:
package asc.mixins;
import org.apache.tapestry5.RenderSupport;
import org.apache.tapestry5.annotations.AfterRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.IncludeJavaScriptLibrary;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.corelib.base.AbstractTextField;
#IncludeJavaScriptLibrary("js_dasher_mixin.js")
public class DasherMixin {
#Environmental
private RenderSupport renderSupport;
#InjectContainer
private AbstractTextField field;
#AfterRender
void addScript() {
this.renderSupport.addScript("new JSDasher('%s');",
this.field.getClientId());
}
}
The Javascript mixin file:
JSDasher = Class.create({
initialize: function(textField)
{
this.textField = $(textField);
this.textField.observe('focus', this.onFocus.bindAsEventListener(this));
},
onFocus: function(event)
{
//call my javascript init() function
}
}
part of my javascript file I wish to run when the textfield gains focus:
var posX, posY;
// Sets up our global variables and dispatches an init request to the server.
function init() {
posX=0;
posY=0;
canvas = document.getElementById("canvas");
canvasWidth = canvas.offsetWidth;
canvasHeight = canvas.offsetHeight;
if (canvas.getContext) {
ctx = canvas.getContext("2d");
}
canvas.onclick = canvasClicked;
canvas.onmousemove = mouseMoved;
canvasOffsetX = findPosX(canvas);
canvasOffsetY = findPosY(canvas);
sessID = -1;
sendInitRQ(canvasWidth, canvasHeight);
}
My javascript file is much larger than above, my question is where should I put my javascript code above?
Should it all be contained in the mixin.js file? if so where exactly should it go?
Thanks in advance for any help.
The method is ok free-floating in mixin.js but with name like init, you might have a conflict. You can put it in the JSDasher class itself or move the body to the onFocus function. You can also define any other functions on the JSDasher class and call them with this.function_name. Look at datefield.js in the Tapestry source as an example.