/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.examples.ajax.builtin;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.util.io.IClusterable;
/**
* Ajax todo list without having to write any JavaScript yourself.
*
* @author Martijn Dashorst
*/
public class TodoList extends BasePage
{
/**
* The todo object.
*/
public static class TodoItem implements IClusterable
{
private static final long serialVersionUID = 1L;
/** Is the item done? */
private boolean checked;
/** Description of the item. */
private String text;
/** Constructor. */
public TodoItem()
{
}
/**
* Copy constructor.
*
* @param item
* the item to copy the values from.
*/
public TodoItem(TodoItem item)
{
text = item.text;
}
/**
* @return Returns the checked property.
*/
public boolean isChecked()
{
return checked;
}
/**
* Sets the checked property.
*
* @param checked
* The checked property to set.
*/
public void setChecked(boolean checked)
{
this.checked = checked;
}
/**
* Gets the description of the item.
*
* @return Returns the text.
*/
public String getText()
{
return text;
}
/**
* Sets the description of the item.
*
* @param text
* The text to set.
*/
public void setText(String text)
{
this.text = text;
}
}
/**
* Container for displaying the todo items in a list.
*/
public class TodoItemsContainer extends WebMarkupContainer
{
/**
* Constructor.
*
* @param id
* the component identifier.
*/
public TodoItemsContainer(String id)
{
super(id);
// let wicket generate a markup-id so the contents can be
// updated through an AJAX call.
setOutputMarkupId(true);
// add the listview to the container
add(new ListView<TodoItem>("item", items)
{
@Override
protected void populateItem(ListItem<TodoItem> item)
{
// add an AJAX checkbox to the item
item.add(new AjaxCheckBox("check", new PropertyModel<>(
item.getModel(), "checked"))
{
@Override
protected void onUpdate(AjaxRequestTarget target)
{
// no need to do anything, the model is updated by
// itself, and we don't have to re-render a
// component (the client already has the correct
// state).
}
});
// display the text of the todo item
item.add(new Label("text", new PropertyModel<String>(item.getModel(), "text")));
}
});
}
}
/**
* Container for showing either the add link, or the addition form.
*/
private class AddItemsContainer extends WebMarkupContainer
{
/** Visibility toggle so that either the link or the form is visible. */
private boolean linkVisible = true;
/** Link for displaying the AddTodo form. */
private final class AddTodoLink extends AjaxFallbackLink<Void>
{
/** Constructor. */
private AddTodoLink(String id)
{
super(id);
}
/**
* onclick handler.
*
* @param targetOptional
* the request target.
*/
@Override
public void onClick(Optional<AjaxRequestTarget> targetOptional)
{
onShowForm(targetOptional);
}
/**
* Toggles the visibility with the add form.
*
* @return <code>true</code> when the add links is visible and the form isn't.
*/
@Override
public boolean isVisible()
{
return linkVisible;
}
}
/**
* Link for removing all completed todos from the list, this link follows the same
* visibility rules as the add link.
*/
private final class RemoveCompletedTodosLink extends AjaxFallbackLink<Void>
{
/**
* Constructor.
*
* @param id
* component id
*/
public RemoveCompletedTodosLink(String id)
{
super(id);
}
@Override
public void onClick(Optional<AjaxRequestTarget> targetOptional)
{
onRemoveCompletedTodos(targetOptional);
}
/**
* Toggles the visibility with the add form.
*
* @return <code>true</code> when the add links is visible and the form isn't.
*/
@Override
public boolean isVisible()
{
return linkVisible;
}
}
/**
* Displays a form which offers an edit field and two buttons: one for adding the todo item,
* and one for canceling the addition. The visibility of this component is mutual exclusive
* with the visibility of the add-link.
*/
private final class AddTodoForm extends Form<TodoItem>
{
/**
* Constructor.
*
* @param id
* the component id.
*/
public AddTodoForm(String id)
{
super(id, new CompoundPropertyModel<>(new TodoItem()));
setOutputMarkupId(true);
add(new TextField<>("text"));
add(new AjaxButton("add", this)
{
@Override
protected void onSubmit(AjaxRequestTarget target)
{
// retrieve the todo item
TodoItem item = (TodoItem)getParent().getDefaultModelObject();
// add the item
onAdd(item, target);
}
@Override
protected void onError(AjaxRequestTarget target)
{
}
});
add(new AjaxButton("cancel", this)
{
@Override
public void onSubmit(AjaxRequestTarget target)
{
onCancelTodo(target);
}
@Override
protected void onError(AjaxRequestTarget target)
{
}
});
}
/**
* Toggles the visibility with the add link. When the link is visible, the form isn't.
*
* @return true when the form is visible and the link isn't.
*/
@Override
public boolean isVisible()
{
return !linkVisible;
}
}
/**
* Constructor.
*
* @param id
* the component id.
*/
public AddItemsContainer(String id)
{
super(id);
// let wicket generate a markup-id so the contents can be
// updated through an AJAX call.
setOutputMarkupId(true);
add(new AddTodoLink("link"));
add(new RemoveCompletedTodosLink("remove"));
add(new AddTodoForm("form"));
}
/**
* Called then the add link was clicked, shows the form, and hides the link.
*
* @param targetOptional
* the request target.
*/
void onShowForm(Optional<AjaxRequestTarget> targetOptional)
{
// toggle the visibility
linkVisible = false;
// redraw the add container.
targetOptional.ifPresent(target -> target.add(this));
}
void onRemoveCompletedTodos(Optional<AjaxRequestTarget> targetOptional)
{
List<TodoItem> ready = new ArrayList<>();
for (TodoItem todo : items)
{
if (todo.isChecked())
{
ready.add(todo);
}
}
items.removeAll(ready);
targetOptional.ifPresent(target -> {
// repaint our panel
target.add(this);
// repaint the listview as there was a new item added.
target.add(showItems);
});
}
/**
* Called when the form is submitted through the add button, stores the todo item, hides the
* form, displays the add link and updates the listview.
*
* @param target
* the request target
*/
void onAdd(TodoItem item, AjaxRequestTarget target)
{
// add the item
items.add(new TodoItem(item));
// reset the model
item.setChecked(false);
item.setText("");
// toggle the visibility
linkVisible = true;
// repaint our panel
target.add(this);
// repaint the listview as there was a new item added.
target.add(showItems);
}
/**
* Called when adding a new todo item was canceled. Hides the add form and displays the add
* link.
*
* @param target
* the request target.
*/
void onCancelTodo(AjaxRequestTarget target)
{
// toggle the visibility
linkVisible = true;
// repaint the panel.
target.add(this);
}
}
/**
* Container for redrawing the todo items list with an AJAX call.
*/
private final WebMarkupContainer showItems;
/**
* The list of todo items.
*/
final List<TodoItem> items = new ArrayList<>();
/**
* Constructor.
*/
public TodoList()
{
// add the listview container for the todo items.
showItems = new TodoItemsContainer("showItems");
add(showItems);
add(new AjaxFallbackLink<Void>("ajaxback")
{
@Override
public void onClick(Optional<AjaxRequestTarget> targetOptional)
{
setResponsePage(getPage());
}
});
// add the add container for the todo items.
add(new AddItemsContainer("addItems"));
}
@Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
response.render(
CssHeaderItem.forReference(new CssResourceReference(TodoList.class, "todo.css")));
}
}