How to handle changes in a form with a model-update using Wicket and AJAX - ajax

I have a form with many input-fields and need to handle a change to any of those input-fields; so I add a AjaxEventBehavior to the form, like:
Form<MyX> myForm = new Form<>("X", getModel());
myForm.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
handleFormChange(...);
}
});
The method handleFormChange gets called everytime I change some content in the input-fields of the form. But the model is not getting updated with the new value of the changed input-field of the form.
How can I get thoose model-updates? I tried AjaxFormComponentUpdatingBehavior. It updates the model, but I cannot use it for forms, just for FormComponents.
Does anybody has an ideas how to handle that? TIA!

With AjaxFormSubmitBehavior you can submit the whole form on each change.

First for on change use the dedicated OnChangeAjaxBehavior.
Then you can use the Iterator of the form to get all children and add then add the OnChangeAjaxBehavior to all FormComponents which will call your handleFormChange() on every change like this:
for (Iterator it = form.iterator(); it.hasNext();) {
Object o = it.next();
if (o instanceof FormComponent) {
((FormComponent) o).add(new OnChangeAjaxBehavior() {
#Override
protected void onUpdate(AjaxRequestTarget target) {
handleFormChange(...);
}
});
}
}

Related

MudTable Default SelectedItem

I am trying to setup a table from MudBlazor (MudTable) with single row select but I can't seem to find a way on how I could set the default item value. The code I have is fairly similar to this example in their website. Except on how I load the data, I am using ServerData property instead of Items.
Tried adding something like on "OnAfterRender", "OnInitialized", etc... but no luck.
protected override OnAfterRender(bool firstRender)
{
table.SelectedItem = MyDesiredDefaultValue;
//or
table.SetSelectedItem(MyDesiredDefaultValue);
}
Add a StateHasChanged call to your code.
Example
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
table.SelectedItem = MyDesiredDefaultValue;
StateHasChanged();
}
}
Demo
https://try.mudblazor.com/snippet/cummYIPSLfOzxLxx

Call several different JavaScript within AjaxLink one after the other

When I click on an AjaxLink, I would like to have a validation via JavaScript on the client side first (because the LocalStorage is queried) and then depending on the result, further JavaScript calls are made. How can i achieve this?
In a pseudo code it would look like this:
new AjaxLink<>("myId", myModel) {
#Override
public void onClick(AjaxRequestTarget target) {
boolean isCounterValid = target.appendJavaScript(checkCounter()); // i know that this is not possible, therefore pseudo code
if(isCounterValid) {
target.appendJavaScript(someOtherJavaScript());
}
else {
target.appendJavaScript(anotherJavaScript());
}
}
private String checkCounter() {
return "var count = window.localStorage.getItem('myCounter'); return count !== 1;";
}
private String someOtherJavaScript() {
return "change something";
}
private String anotherJavaScript() {
return "change other thing";
}
};
You need to send extra request parameters with the Ajax call when the link is clicked. For that you should override updateAjaxAttributes(AjaxRequestAttributes attributes) method of AjaxLink:
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
attributes.getDynamicExtraParameters().add("var count = window.localStorage.getItem('myCounter'); return [{\"name\":\"count\", \"value\": count}]");
}
This way inside AjaxLink#onClick() you can read the count via:
int count = getRequest().getRequestParameters().getParameterValue("count").toInt();
AJAX components and behaviors can customize AJAX attributes overriding updateAjaxAttributes and using a custom implementation of AjaxCallListener which exposes different method to hook into the AJAX request cycle. In you case you could use AjaxCallListener#getBeforeSendHandler.
For a full introduction to this topic (with examples) see user guide:
https://ci.apache.org/projects/wicket/guide/8.x/single.html#_ajax_request_attributes_and_call_listeners

TempData not kept between postback

I need some advice on how to proceed with the mvc app I'm building. On my page I type out who is logged in to the page. This I first did by creating a base class where I created a user class which contained the users name and a image representing the user. Then I passed this class on to my views. But I also need to pass other models to my views depending on what view I'm in. Sure I could build a class that contain all different models I need to use on each page but there should be a easy way to pass name and image values across the pages and be persistant? I tried TempData together with TempData.Keep() but that was not persistant. How can I pass theses values between pages?
public ActionResult Validate(AccountModels.LoginModel model)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var mu = _repo.GetUser(Membership.GetUser().ProviderUserKey.ToString());
TempData["Name"] = mu.Name;
TempData["Image"] = mu.Image;
TempData.Keep();
FormsAuthentication.RedirectFromLoginPage(model.UserName, model.RememberMe);
}
}
return View("Index");
}
As #Jyoti said, you could use of Keep() method.
To make it easy to work with TempData, I wrote these methods in my BaseController, and I use it in every controller when I need to transfer data between actions or between view and controller.
protected TReturnType GetTempDataValue<TReturnType>(PsmConstants.TempDataKey sessionName, bool peekData =false )
{
object value = peekData ? TempData.Peek(sessionName.ToString()) : TempData[sessionName.ToString()];
return (TReturnType) value;
}
protected void RemoveTempData(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()) && TempData[sessionName.ToString()] == null) return;
TempData[sessionName.ToString()] = null;
}
protected void SetTempDataValue(PsmConstants.TempDataKey sessionName, object value)
{
if(TempData.ContainsKey(sessionName.ToString()))
TempData[sessionName.ToString()]=null;
TempData[sessionName.ToString()] = value;
}
protected void KeepTempDataValue(PsmConstants.TempDataKey sessionName)
{
if (TempData.ContainsKey(sessionName.ToString()))
TempData.Keep(sessionName.ToString());
}
And this is the Keys enumeration :
public enum TempDataKey
{
PageError = 1,
PageInfo = 2
}
And this is, the usage of these methods(Set value and Get value from TempData):
SetTempDataValue(PsmConstants.TempDataKey.PageError , 'your error message' );
var originalValues = GetTempDataValue<MyModel>(PsmConstants.TempDataKey.Info, true);
Use session instead of Temp if it is not working.but i think it should work.
TempData["Name"] = mu.Name;TempData["Image"] = mu.Image;TempData.Keep();
How you are passing this into other models,Please share the source code so that it will easy to identify.

Wicket: form submit with reusable components and AjaxButton

I'm using wicket version 6.
I have a form. as contents of the form, I have a FormComponentPanel that contains two DateTimeField's (org.apache.wicket.extensions.yui.calendar).
In the class containing the form I have one AjaxButton, and one AjaxLink, both doing the same: reading the models, creating a new object and sending it to some server for processing.
Anyhow,
when clicking on the Link my new object is created with the correct values except those newly dates selected with the datepicker
when clicking the Button I get some error ([AjaxRequestHandler#1701777932 responseObject [org.apache.wicket.ajax.AjaxRequestHandler$1#3e1]) but no further information on the error
well, I addressed the first issue (Link) with trying to add ajax update behavior to it, as suggested here , but the selected date is not updated in the model
the AjaxButton is created and onSubmit overwritten with just calling another method and target.add(form); also setOutputMarkupId is set to true, but it seems that still something is missing
In order to get it to work I'd just need to solve one of the problems, but it would be great if someone has a solution for both problems. Thanks in advance.
edit
public MyPanelIncludingForm() {
// ...
form.add(getRangePanel()); // creates a new TimeRangePanel and returns the instance
form.add(getSubmitButton());
// ...
}
private FormComponent<String> getSubmitButton() {
FormComponent<String> submitBtn = new AjaxButton("submitBtn", form) {
private static final long serialVersionUID = 3005L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
System.out.println("submit QT ajax button");
setResponsePage(HomePage.class);
sendQuery();
target.add(form);
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
System.err.println("error occurred. " + target);
target.add(feedback);
}
};
submitBtn.setOutputMarkupId(true);
return submitBtn;
}
// separate FormComponentModel for timeRange
public class TimeRangePanel extends FormComponentPanel<MyRange> {
MyRange range;
PropertyModel<Date> dpFromPM = new PropertyModel<Date>(this, "range.start");
PropertyModel<Date> dpToPM = new PropertyModel<Date>(this, "range.stop");
public RangePanel(String id, IModel<MyRange> model) {
super(id, model);
dpFrom = new DateTimeField("dpFrom", dpFromPM) {
private static final long serialVersionUID = 3006L;
#Override
protected DateTextField newDateTextField(String id, PropertyModel<Date> model) {
DateTextField dtf = super.newDateTextField(id, model);
AjaxFormComponentUpdatingBehavior a = new AjaxFormComponentUpdatingBehavior("onChange") {
private static final long serialVersionUID = 3006L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
System.out.println("here u " + dpFrom.getModelObject().toString());
}
};
dtf.add(a);
return dtf;
}
};
// second DateTimeField as dpFrom
} // end of constructor
#Override
protected void onBeforeRender() {
range = getModelObject();
super.onBeforeRender();
}
} // end of class
edit2
this is what the wicket ajax debug window is printing:
INFO: focus removed from
INFO: focus set on submitBtn11
INFO: Received ajax response (299 characters)
INFO:
<div wicket:id="feedbackQuery" class="feedback" id="feedbackQuery1c"><wicket:panel>
<ul wicket:id="feedbackul" class="feedbackPanel">
<li wicket:id="messages" class="feedbackPanelERROR">
<span wicket:id="message" class="feedbackPanelERROR"></span>
</li>
</ul>
</wicket:panel></div>
INFO: returned focused element: [object HTMLInputElement]
INFO: returned focused element: [object HTMLInputElement]
INFO: Response processed successfully.
INFO: refocus last focused component not needed/allowed
INFO: focus removed from submitBtn11
edit3
As I wrote in a comment:
I removed the re-usable components (FormComponentPanel) and now I don't get an error with the AjaxButton . anyhow, it's weird, I thought re-usable components should work, even with Ajax; also the models were assigned properly.
when clicking the Button I get some error
([AjaxRequestHandler#1701777932 responseObject
[org.apache.wicket.ajax.AjaxRequestHandler$1#3e1]) but no further
information on the error
=> You should run Wicket in DEVELOPMENT mode to get a more detailed trace.
You might simply add in your form a field called:
range.start
range.stop
and use the Object MyRange as property model instead create a new one for each field.
There is actually no need for that extra cycle. And I don't think Wicket will write the information back to the MyRange object if you create new PropertyModels for every attribute.
For example: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/admin/users/UserForm.java?view=markup
Line 276
and the corresponding HTML
http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/admin/users/UsersPanel.html?view=markup
Line 82
Sebastian

wicket - Implement Ajax add/remove items ListView

Im getting crazy about this issue. I implemented a ListView which you can add/remove TextField dinamically, but only the last TextField is removed.
An example:
// Object type which is used in the list
public class ExampleObject implements Serializable{
private String keyword;
public String getKeyword() {
return this.keyword;
}
public void setKeyword(String s) {
keyword = s;
}
}
//ListView
List<ExampleObject> keywordList = new ArrayList<ExampleObject>();
keywordList.add(new ExampleObject());
ListView keywordView = new ListView("keywordView", keywordList) {
#Override
protected void populateItem(final ListItem item) {
ExampleObject model = (ExampleObject) item.getModelObject();
item.add(new TextField("subKeyword", new PropertyModel(model, "keyword")));
// keyword remove link
AjaxSubmitLink removeKeyword = new AjaxSubmitLink("removeKeyword", myForm)
{
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
ExampleObject selected = (ExampleObject) item.getModelObject();
// I also tried deleting by index. println shows the
// selected object is the element I want to remove, so why always
// remove last object of the list?
keywordList.remove(selected);
if (target != null) {
target.addComponent(myForm);
}
}
};
item.add(removeKeyword);
// keyword add link
AjaxSubmitLink addKeyword = new AjaxSubmitLink("addKeyword", metadataForm)
{
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
keywordList.add(new ExampleObject());
if (target != null) {
target.addComponent(myForm);
}
}
};
item.add(addKeyword);
}
keywordView.setReuseItems(true);
metadataForm.add(keywordView);
Any help would be very appreciate, because I thing this issue is really a very stupid mistake but I cant get it!
Thanks
It might be as simple as getting rid of the line
keywordView.setReuseItems(true);
The reuseItems flag is an efficiency so that the page does not rebuild the ListView items unnecessarily, but it can lead to confusion such as what you're seeing.
ListView really wasn't made for use with forms though, and you'll probably be better off with another tactic entirely.
This blog entry on building a list editor form component might be useful. It will need some changes if you're not on Wicket 1.4, but similar stuff is definitely possible in Wicket 1.3, and the comments have some hints.
Read the javadoc of ListView#setReuseItems():
"But if you modify the listView model object, than you must manually call listView.removeAll() in order to rebuild the ListItems."
You can not use a ListView this way. Either use the members of ListView provided:
removeLink(java.lang.String id, ListItem<T> item)
and
newItem(int index)
but, I never used those. If I have to display a List and be able to add remove Items dynamically, I prefer the RefreshingView.
If you do use FormComponents inside a RefreshingView, make sure you set a Reusestartegy (setItemReuseStrategy())
Bert

Resources