001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.extensions.markup.html.repeater.data.table.filter; 018 019import org.apache.wicket.Component; 020import org.apache.wicket.behavior.Behavior; 021import org.apache.wicket.markup.ComponentTag; 022import org.apache.wicket.markup.MarkupStream; 023import org.apache.wicket.markup.head.IHeaderResponse; 024import org.apache.wicket.markup.head.JavaScriptHeaderItem; 025import org.apache.wicket.markup.head.OnEventHeaderItem; 026import org.apache.wicket.markup.head.OnLoadHeaderItem; 027import org.apache.wicket.markup.html.form.Form; 028import org.apache.wicket.markup.html.form.FormComponent; 029import org.apache.wicket.request.resource.JavaScriptResourceReference; 030import org.apache.wicket.request.resource.ResourceReference; 031import org.apache.wicket.util.string.Strings; 032 033/** 034 * A form with filter-related special functionality for its form components. 035 * 036 * <p> 037 * This form uses an invisible button to be able to submit when the user presses the <em>ENTER</em> 038 * key. If there is a need to add an explicit 039 * {@link org.apache.wicket.markup.html.form.IFormSubmittingComponent} to this form then 040 * {@link Form#setDefaultButton(org.apache.wicket.markup.html.form.IFormSubmittingComponent)} should 041 * be used to specify this custom submitting component. 042 * </p> 043 * 044 * @param <T> 045 * type of filter state object 046 * @author igor 047 */ 048public class FilterForm<T> extends Form<T> 049{ 050 private static final long serialVersionUID = 1L; 051 052 private static final ResourceReference JS = new JavaScriptResourceReference(FilterForm.class, 053 "wicket-filterform.js"); 054 055 private final IFilterStateLocator<T> locator; 056 057 /** 058 * @param id 059 * component id 060 * @param locator 061 * filter state locator 062 */ 063 public FilterForm(final String id, final IFilterStateLocator<T> locator) 064 { 065 super(id, new FilterStateModel<>(locator)); 066 067 this.locator = locator; 068 } 069 070 @Override 071 public void renderHead(final IHeaderResponse response) 072 { 073 super.renderHead(response); 074 075 response.render(JavaScriptHeaderItem.forReference(JS)); 076 077 response.render(OnLoadHeaderItem.forScript(String.format( 078 "Wicket.FilterForm.restore('%s');", getFocusTrackerFieldCssId()))); 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) 086 { 087 super.onComponentTagBody(markupStream, openTag); 088 089 getResponse().write(generateHiddenInputMarkup()); 090 } 091 092 /** 093 * Generates the Markup for the hidden input. Can be overridden by subclasses if necessary. 094 * 095 * @return The markup to be appended to the response 096 */ 097 protected String generateHiddenInputMarkup() { 098 String id = Strings.escapeMarkup(getFocusTrackerFieldCssId()).toString(); 099 String value = getRequest().getPostParameters().getParameterValue(id).toString(""); 100 String cssClass = getString(Form.HIDDEN_FIELDS_CSS_CLASS_KEY); 101 102 return String.format( 103 "<div hidden='' class='%s'><input type='hidden' name='%s' id='%s' value='%s'/><input type='submit'/></div>", 104 cssClass, id, id, Strings.escapeMarkup(value)); 105 } 106 107 /** 108 * @return css id of the hidden form input that keeps track of the focused input field 109 */ 110 public final String getFocusTrackerFieldCssId() 111 { 112 return getMarkupId() + "focus"; 113 } 114 115 /** 116 * @return IFilterStateLocator passed to this form 117 */ 118 public final IFilterStateLocator<T> getStateLocator() 119 { 120 return locator; 121 } 122 123 /** 124 * Adds behavior to the form component to allow this form to keep track of the component's focus 125 * which will be restored after a form submit. 126 * 127 * @param fc 128 * form component 129 */ 130 public final void enableFocusTracking(final FormComponent<?> fc) 131 { 132 fc.add(new Behavior() 133 { 134 private static final long serialVersionUID = 1L; 135 136 @Override 137 public void bind(Component component) 138 { 139 super.bind(component); 140 component.setOutputMarkupId(true); 141 } 142 143 @Override 144 public void renderHead(Component component, IHeaderResponse response) { 145 response.render(OnEventHeaderItem.forComponent(component, "focus", 146 getFocusTrackingHandler(component))); 147 } 148 }); 149 } 150 151 /** 152 * Returns the javascript focus handler necessary to notify the form of focus tracking changes 153 * on the component 154 * 155 * Useful when components want to participate in focus tracking but want to add the handler 156 * their own way. 157 * 158 * A unique css id is required on the form component for focus tracking to work. 159 * 160 * @param component 161 * component to 162 * @return the javascript focus handler necessary to notify the form of focus tracking changes 163 * on the component 164 */ 165 public final String getFocusTrackingHandler(final Component component) 166 { 167 return String.format("Wicket.FilterForm.focused(this, '%s');", getFocusTrackerFieldCssId()); 168 } 169}