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.OnLoadHeaderItem; 026import org.apache.wicket.markup.html.form.Form; 027import org.apache.wicket.markup.html.form.FormComponent; 028import org.apache.wicket.request.resource.JavaScriptResourceReference; 029import org.apache.wicket.request.resource.ResourceReference; 030import org.apache.wicket.util.string.Strings; 031 032/** 033 * A form with filter-related special functionality for its form components. 034 * 035 * <p> 036 * This form uses an invisible button to be able to submit when the user presses the <em>ENTER</em> 037 * key. If there is a need to add an explicit 038 * {@link org.apache.wicket.markup.html.form.IFormSubmittingComponent} to this form then 039 * {@link Form#setDefaultButton(org.apache.wicket.markup.html.form.IFormSubmittingComponent)} should 040 * be used to specify this custom submitting component. 041 * </p> 042 * 043 * @param <T> 044 * type of filter state object 045 * @author igor 046 */ 047public class FilterForm<T> extends Form<T> 048{ 049 private static final long serialVersionUID = 1L; 050 051 private static final ResourceReference JS = new JavaScriptResourceReference(FilterForm.class, 052 "wicket-filterform.js"); 053 054 private final IFilterStateLocator<T> locator; 055 056 /** 057 * @param id 058 * component id 059 * @param locator 060 * filter state locator 061 */ 062 public FilterForm(final String id, final IFilterStateLocator<T> locator) 063 { 064 super(id, new FilterStateModel<>(locator)); 065 066 this.locator = locator; 067 } 068 069 @Override 070 public void renderHead(final IHeaderResponse response) 071 { 072 super.renderHead(response); 073 074 response.render(JavaScriptHeaderItem.forReference(JS)); 075 076 response.render(OnLoadHeaderItem.forScript(String.format( 077 "Wicket.FilterForm.restore('%s');", getFocusTrackerFieldCssId()))); 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 @Override 084 public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) 085 { 086 super.onComponentTagBody(markupStream, openTag); 087 088 getResponse().write(generateHiddenInputMarkup()); 089 } 090 091 /** 092 * Generates the Markup for the hidden input. Can be overridden by subclasses if necessary. 093 * 094 * @return The markup to be appended to the response 095 */ 096 protected String generateHiddenInputMarkup() { 097 String id = Strings.escapeMarkup(getFocusTrackerFieldCssId()).toString(); 098 String value = getRequest().getPostParameters().getParameterValue(id).toString(""); 099 String cssClass = getString(Form.HIDDEN_FIELDS_CSS_CLASS_KEY); 100 101 return String.format( 102 "<div hidden='' class='%s'><input type='hidden' name='%s' id='%s' value='%s'/><input type='submit'/></div>", 103 cssClass, id, id, Strings.escapeMarkup(value)); 104 } 105 106 /** 107 * @return css id of the hidden form input that keeps track of the focused input field 108 */ 109 public final String getFocusTrackerFieldCssId() 110 { 111 return getMarkupId() + "focus"; 112 } 113 114 /** 115 * @return IFilterStateLocator passed to this form 116 */ 117 public final IFilterStateLocator<T> getStateLocator() 118 { 119 return locator; 120 } 121 122 /** 123 * Adds behavior to the form component to allow this form to keep track of the component's focus 124 * which will be restored after a form submit. 125 * 126 * @param fc 127 * form component 128 */ 129 public final void enableFocusTracking(final FormComponent<?> fc) 130 { 131 fc.add(new Behavior() 132 { 133 private static final long serialVersionUID = 1L; 134 135 @Override 136 public void bind(Component component) 137 { 138 super.bind(component); 139 component.setOutputMarkupId(true); 140 } 141 142 @Override 143 public void onComponentTag(final Component component, final ComponentTag tag) 144 { 145 tag.put("onfocus", getFocusTrackingHandler(component)); 146 147 super.onComponentTag(component, tag); 148 } 149 }); 150 } 151 152 /** 153 * Returns the javascript focus handler necessary to notify the form of focus tracking changes 154 * on the component 155 * 156 * Useful when components want to participate in focus tracking but want to add the handler 157 * their own way. 158 * 159 * A unique css id is required on the form component for focus tracking to work. 160 * 161 * @param component 162 * component to 163 * @return the javascript focus handler necessary to notify the form of focus tracking changes 164 * on the component 165 */ 166 public final String getFocusTrackingHandler(final Component component) 167 { 168 return String.format("Wicket.FilterForm.focused(this, '%s');", getFocusTrackerFieldCssId()); 169 } 170}