You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@wicket.apache.org by Peter Henderson <pe...@starjar.com> on 2012/05/04 11:12:52 UTC

Field with OnChangeAjaxBehavior Not updating.

I am having a bit of trouble making a required form field update from an
ajax button.

The field is marked required and has an OnChangeAjaxBehavior.

The user deletes the contents of the field
Presses an AjaxButton (todo some DB work which yields a new value)
Ajax button sets a new value,
The updated value is not pushed to the screen.


I've got a quick start example.
Tested with Wicket 1.5.5

All help is appreciated.



==== HomePage.java ====

package com.mycompany;

import java.math.BigDecimal;
import java.util.Locale;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.request.mapper.parameter.PageParameters;
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.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.convert.IConverter;

public class HomePage extends WebPage {

  /**
   * Simulate values from the DB
   */
  private BigDecimal fromDBWith = BigDecimal.valueOf(500);
  private BigDecimal fromDBWithout = BigDecimal.valueOf(600);


  private BigDecimal withPrice = BigDecimal.valueOf(100);
  private BigDecimal withoutPrice = BigDecimal.valueOf(200);


  Model<BigDecimal> withModel = new Model<BigDecimal>() {
    @Override public BigDecimal getObject() { return withPrice; }
    @Override public void setObject(BigDecimal newVal) { withPrice =
newVal; }
  };

  Model<BigDecimal> withoutModel = new Model<BigDecimal>() {
    @Override public BigDecimal getObject() { return withoutPrice; }
    @Override public void setObject(BigDecimal newVal) { withoutPrice =
newVal; }
  };




  public HomePage(final PageParameters parameters) {

    Form form = new Form("someForm");
    add(form);

    final FeedbackPanel feedback = new FeedbackPanel("feedback");
    feedback.setOutputMarkupId(true);
    form.add(feedback);

    final MoneyTextField withTF = new MoneyTextField("withTF", withModel);
    withTF.setOutputMarkupId(true);

    // Required AND OnChangeAjaxBehavior means I cant update the form field
from the button
    // when the field is empty.
    withTF.setRequired(true);
    withTF.add(new OnChangeAjaxBehavior() {
      @Override public void onUpdate(AjaxRequestTarget art) {
        System.out.println("In withTF onUpdate " + withPrice);
      }
    });
    form.add(withTF);


    final MoneyTextField withoutTF = new MoneyTextField("withoutTF",
withoutModel);
    withoutTF.setRequired(true);
    withoutTF.setOutputMarkupId(true);
    form.add(withoutTF);

    AjaxLink withBtn = new AjaxLink("withBtn") {

      @Override
      public void onClick(AjaxRequestTarget art) {
        System.out.println("In withBtn onClick " + withPrice);
        fromDBWith = fromDBWith.add(BigDecimal.ONE);
        withPrice = fromDBWith;
        art.add(withTF, feedback);
      }
    };
    form.add(withBtn);


    AjaxLink withoutBtn = new AjaxLink("withoutBtn") {
      @Override
      public void onClick(AjaxRequestTarget art) {
        System.out.println("In withoutBtn onClick " + withoutPrice);
        fromDBWithout = fromDBWithout.add(BigDecimal.ONE);
        withoutPrice = fromDBWithout;
        art.add(withoutTF, feedback);
      }
    };
    form.add(withoutBtn);
  }
}


/**
 *
 * @author peter
 */
class MoneyTextField extends TextField<BigDecimal> {


  public MoneyTextField(String id, IModel<BigDecimal> model) {
    super(id, model, BigDecimal.class);
    add(AttributeModifier.append("class", "money"));
  }

  @Override public String getInputType() { return "number"; }

  @Override public <C> IConverter<C> getConverter(Class<C> ctype) {
    if (BigDecimal.class.isAssignableFrom(ctype)) {
      MoneyConverter mc = new MoneyConverter(false, false);

      IConverter<C> res = (IConverter<C>)mc;
      return res;
      //return mc.asInstanceOf<IConverter<C>>;
    } else {
      System.out.println("MoneyConverter NOT ASSIGNABLE FROM " + ctype);
      return super.getConverter(ctype);
    }
  }
}

/**
 *
 * @author peter
 */
class MoneyConverter implements IConverter<BigDecimal> {

  private Boolean blankWhenZero = false;
  private Boolean negativeShowCR = false;

  public MoneyConverter(Boolean blankWhenZero, Boolean negativeShowCR)  {
    this.blankWhenZero = blankWhenZero;
    this.negativeShowCR = negativeShowCR;
  }

  @Override public BigDecimal convertToObject(String string, Locale locale)
{
    //if (StringUtils.isBlank(string)) {
    if (string==null || string.length()==0) {
      //return BigDecimal.ZERO;
      return null;
    }
    return new BigDecimal(string);
  }

  @Override public String convertToString(BigDecimal o, Locale locale) {

    System.out.println("MoneyConverter o=" + o);

    if (o==null) {
      return "";
    }

    if (blankWhenZero && o.compareTo(BigDecimal.ZERO)==0) {
      return "";
    }

    if (negativeShowCR && o.compareTo(BigDecimal.ZERO)<0) {
      return String.format("%.2f CR", o.abs());
    }

    return String.format("%.2f", o);
  }
}



===== HomePage.html =====
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
    <head>
        <meta charset="utf-8" />
        <title>Apache Wicket Quickstart</title>
        <link href='
http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:regular,bold'
rel='stylesheet' type='text/css' />
        <link rel="stylesheet" href="style.css" type="text/css"
media="screen" title="Stylesheet" />
    </head>
    <body>

    <form wicket:id="someForm">

      <div wicket:id="feedback"></div>

      <table>

        <tr>
          <td>With OnUpdate</td>
          <td>Without OnUpdate</td>
        </tr>

        <tr>
          <td><input wicket:id="withTF" type="number"></td>
          <td><input wicket:id="withoutTF" type="number"></td>
        </tr>

        <tr>
          <td><button wicket:id="withBtn">Press Me</button></td>
          <td><button wicket:id="withoutBtn">Press Me</button></td>
        </tr>

        <tr>
          <td>
            This TextField has required=True and an OnChangeAjaxBehavior<br>
            Press Button, everything works fine<br>
            Empty the text field (blank value), then press button,<br>
            field does not get updated to the value loaded from the DB
          </td>
          <td>
            This TextField has required=True but no OnChangeAjaxBehavior<br>
            Press Button, everything works fine<br>
            empty the text field, then press button updates OK.
          </td>
        </tr>


        <tr>
          <td colspan="2">
            Is this a bug or am I doing something wrong?
          </td>
        </tr>

      </table>
    </form>


    </body>
</html>




-- 
Peter Henderson

Re: Field with OnChangeAjaxBehavior Not updating.

Posted by jensiator <je...@gmail.com>.
In the AjaxLink you change the model and I think your model becomes out of
sync with the formcomponent.  I think the formcomponent don't know which
value it should use. The one the end user typed or the model that has
changed. Sometimes it helps to think about when you want the model to be
update. I personally thinks one should try to update the models as little as
possibly with ajax. I think the most web users expect the data to be updated
when the form is submitted. Not on events like unblur and onchange. But you
can't always avoid it. 
One way of doing it could be to let the Ajaxlink only update the text value
in the textfield. The textfield will not be set to the model until you
submit. You would need to introduce a ordinary form submit button.
And you would need to divide the Textfields model from what the AjaxLink is
changing. 
You will always need to add the textfields to you AjaxRequestTarget art.
But as I see it you got two choices here. 
1. In the Ajaxlink onclick you do your db work and set some property in the
model. The textfield is not binded to the same property. Instead you
override the Textfields onComponentTag method and
pTag.put("value",fromDBWith.toString()). ( toString is not enough you should
use a converter her, Look below )
2.  I would probably try to put the dbcall in the onComponentTag of the
textfield. The reason for this is because it would be great to have the
textfield component in charge for what it will display. If you got
cooworkes, would they look in the AjaxLink if the Textfield was not working
as expected? You would need to do some checks if the AjaxLink have been
clicked then I guess. 

Anyone else got something on this? It depends a little from case to case. I
have created a Behavior for this instead of overriding oncomponenttag.

And about
 @Override public <C> IConverter<C> getConverter(Class<C> ctype) {
    if (BigDecimal.class.isAssignableFrom(ctype)) { 
        MoneyConverter mc = new MoneyConverter(false, false); 

I suggest you change the default BigdecimalConverter on application level
instead. in wicket 1.4. you override newConverterLocator in the Application. 


--
View this message in context: http://apache-wicket.1842946.n4.nabble.com/Field-with-OnChangeAjaxBehavior-Not-updating-tp4608156p4623294.html
Sent from the Users forum mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: Field with OnChangeAjaxBehavior Not updating.

Posted by Andrea Del Bene <ad...@ciseonweb.it>.
Try calling form.clearInput() after you have updated the value of the 
models.
> I am having a bit of trouble making a required form field update from an
> ajax button.
>
> The field is marked required and has an OnChangeAjaxBehavior.
>
> The user deletes the contents of the field
> Presses an AjaxButton (todo some DB work which yields a new value)
> Ajax button sets a new value,
> The updated value is not pushed to the screen.
>
>
> I've got a quick start example.
> Tested with Wicket 1.5.5
>
> All help is appreciated.
>
>
>
> ==== HomePage.java ====
>
> package com.mycompany;
>
> import java.math.BigDecimal;
> import java.util.Locale;
> import org.apache.wicket.AttributeModifier;
> import org.apache.wicket.ajax.AjaxRequestTarget;
> import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
> import org.apache.wicket.ajax.markup.html.AjaxLink;
> import org.apache.wicket.request.mapper.parameter.PageParameters;
> 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.markup.html.panel.FeedbackPanel;
> import org.apache.wicket.model.IModel;
> import org.apache.wicket.model.Model;
> import org.apache.wicket.util.convert.IConverter;
>
> public class HomePage extends WebPage {
>
>    /**
>     * Simulate values from the DB
>     */
>    private BigDecimal fromDBWith = BigDecimal.valueOf(500);
>    private BigDecimal fromDBWithout = BigDecimal.valueOf(600);
>
>
>    private BigDecimal withPrice = BigDecimal.valueOf(100);
>    private BigDecimal withoutPrice = BigDecimal.valueOf(200);
>
>
>    Model<BigDecimal>  withModel = new Model<BigDecimal>() {
>      @Override public BigDecimal getObject() { return withPrice; }
>      @Override public void setObject(BigDecimal newVal) { withPrice =
> newVal; }
>    };
>
>    Model<BigDecimal>  withoutModel = new Model<BigDecimal>() {
>      @Override public BigDecimal getObject() { return withoutPrice; }
>      @Override public void setObject(BigDecimal newVal) { withoutPrice =
> newVal; }
>    };
>
>
>
>
>    public HomePage(final PageParameters parameters) {
>
>      Form form = new Form("someForm");
>      add(form);
>
>      final FeedbackPanel feedback = new FeedbackPanel("feedback");
>      feedback.setOutputMarkupId(true);
>      form.add(feedback);
>
>      final MoneyTextField withTF = new MoneyTextField("withTF", withModel);
>      withTF.setOutputMarkupId(true);
>
>      // Required AND OnChangeAjaxBehavior means I cant update the form field
> from the button
>      // when the field is empty.
>      withTF.setRequired(true);
>      withTF.add(new OnChangeAjaxBehavior() {
>        @Override public void onUpdate(AjaxRequestTarget art) {
>          System.out.println("In withTF onUpdate " + withPrice);
>        }
>      });
>      form.add(withTF);
>
>
>      final MoneyTextField withoutTF = new MoneyTextField("withoutTF",
> withoutModel);
>      withoutTF.setRequired(true);
>      withoutTF.setOutputMarkupId(true);
>      form.add(withoutTF);
>
>      AjaxLink withBtn = new AjaxLink("withBtn") {
>
>        @Override
>        public void onClick(AjaxRequestTarget art) {
>          System.out.println("In withBtn onClick " + withPrice);
>          fromDBWith = fromDBWith.add(BigDecimal.ONE);
>          withPrice = fromDBWith;
>          art.add(withTF, feedback);
>        }
>      };
>      form.add(withBtn);
>
>
>      AjaxLink withoutBtn = new AjaxLink("withoutBtn") {
>        @Override
>        public void onClick(AjaxRequestTarget art) {
>          System.out.println("In withoutBtn onClick " + withoutPrice);
>          fromDBWithout = fromDBWithout.add(BigDecimal.ONE);
>          withoutPrice = fromDBWithout;
>          art.add(withoutTF, feedback);
>        }
>      };
>      form.add(withoutBtn);
>    }
> }
>
>
> /**
>   *
>   * @author peter
>   */
> class MoneyTextField extends TextField<BigDecimal>  {
>
>
>    public MoneyTextField(String id, IModel<BigDecimal>  model) {
>      super(id, model, BigDecimal.class);
>      add(AttributeModifier.append("class", "money"));
>    }
>
>    @Override public String getInputType() { return "number"; }
>
>    @Override public<C>  IConverter<C>  getConverter(Class<C>  ctype) {
>      if (BigDecimal.class.isAssignableFrom(ctype)) {
>        MoneyConverter mc = new MoneyConverter(false, false);
>
>        IConverter<C>  res = (IConverter<C>)mc;
>        return res;
>        //return mc.asInstanceOf<IConverter<C>>;
>      } else {
>        System.out.println("MoneyConverter NOT ASSIGNABLE FROM " + ctype);
>        return super.getConverter(ctype);
>      }
>    }
> }
>
> /**
>   *
>   * @author peter
>   */
> class MoneyConverter implements IConverter<BigDecimal>  {
>
>    private Boolean blankWhenZero = false;
>    private Boolean negativeShowCR = false;
>
>    public MoneyConverter(Boolean blankWhenZero, Boolean negativeShowCR)  {
>      this.blankWhenZero = blankWhenZero;
>      this.negativeShowCR = negativeShowCR;
>    }
>
>    @Override public BigDecimal convertToObject(String string, Locale locale)
> {
>      //if (StringUtils.isBlank(string)) {
>      if (string==null || string.length()==0) {
>        //return BigDecimal.ZERO;
>        return null;
>      }
>      return new BigDecimal(string);
>    }
>
>    @Override public String convertToString(BigDecimal o, Locale locale) {
>
>      System.out.println("MoneyConverter o=" + o);
>
>      if (o==null) {
>        return "";
>      }
>
>      if (blankWhenZero&&  o.compareTo(BigDecimal.ZERO)==0) {
>        return "";
>      }
>
>      if (negativeShowCR&&  o.compareTo(BigDecimal.ZERO)<0) {
>        return String.format("%.2f CR", o.abs());
>      }
>
>      return String.format("%.2f", o);
>    }
> }
>
>
>
> ===== HomePage.html =====
> <!DOCTYPE html>
> <html xmlns:wicket="http://wicket.apache.org">
>      <head>
>          <meta charset="utf-8" />
>          <title>Apache Wicket Quickstart</title>
>          <link href='
> http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:regular,bold'
> rel='stylesheet' type='text/css' />
>          <link rel="stylesheet" href="style.css" type="text/css"
> media="screen" title="Stylesheet" />
>      </head>
>      <body>
>
>      <form wicket:id="someForm">
>
>        <div wicket:id="feedback"></div>
>
>        <table>
>
>          <tr>
>            <td>With OnUpdate</td>
>            <td>Without OnUpdate</td>
>          </tr>
>
>          <tr>
>            <td><input wicket:id="withTF" type="number"></td>
>            <td><input wicket:id="withoutTF" type="number"></td>
>          </tr>
>
>          <tr>
>            <td><button wicket:id="withBtn">Press Me</button></td>
>            <td><button wicket:id="withoutBtn">Press Me</button></td>
>          </tr>
>
>          <tr>
>            <td>
>              This TextField has required=True and an OnChangeAjaxBehavior<br>
>              Press Button, everything works fine<br>
>              Empty the text field (blank value), then press button,<br>
>              field does not get updated to the value loaded from the DB
>            </td>
>            <td>
>              This TextField has required=True but no OnChangeAjaxBehavior<br>
>              Press Button, everything works fine<br>
>              empty the text field, then press button updates OK.
>            </td>
>          </tr>
>
>
>          <tr>
>            <td colspan="2">
>              Is this a bug or am I doing something wrong?
>            </td>
>          </tr>
>
>        </table>
>      </form>
>
>
>      </body>
> </html>
>
>
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org