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.markup.html.form;
018
019import org.apache.wicket.IRequestListener;
020import org.apache.wicket.markup.ComponentTag;
021import org.apache.wicket.markup.head.IHeaderResponse;
022import org.apache.wicket.markup.head.OnEventHeaderItem;
023import org.apache.wicket.model.IModel;
024
025/**
026 * A link which can be used exactly like a Button to submit a Form. The onclick of the link will use
027 * JavaScript to submit the form.
028 * 
029 * <p>
030 * You can use this class 2 ways. First with the constructor without a Form object then this Link
031 * must be inside a Form so that it knows what form to submit to. Second way is to use the Form
032 * constructor then that form will be used to submit to.
033 * </p>
034 * <p>
035 * 
036 * <pre>
037 * Form f = new Form(&quot;linkForm&quot;, new CompoundPropertyModel(mod));
038 *     f.add(new TextField(&quot;value1&quot;));
039 *     f.add(new SubmitLink(&quot;link1&quot;) {
040 *         protected void onSubmit() {
041 *             System.out.println(&quot;Link1 was clicked, value1 is: &quot;
042 *                                 + mod.getValue1());
043 *         };
044 *      });
045 *      add(new SubmitLink(&quot;link2&quot;,f) {
046 *          protected void onSubmit() {
047 *              System.out.println(&quot;Link2 was clicked, value1 is: &quot;
048 *                                 + mod.getValue1());
049 *           };
050 *      });
051 * 
052 *      &lt;form wicket:id=&quot;linkForm&quot; &gt;
053 *          &lt;input wicket:id=&quot;value1&quot; type=&quot;text&quot; size=&quot;30&quot;/&gt;
054 *          &lt;a wicket:id=&quot;link1&quot;&gt;Press link1 to submit&lt;/a&gt;
055 *          &lt;input type=&quot;submit&quot; value=&quot;Send&quot;/&gt;
056 *      &lt;/form&gt;
057 *      &lt;a wicket:id=&quot;link2&quot;&gt;Press link 2 to submit&lt;/a&gt;
058 * </pre>
059 * 
060 * </p>
061 * <p>
062 * If this link is not placed in a form or given a form to cooperate with, it will fall back to a
063 * normal link behavior, meaning that {@link #onSubmit()} will be called without any other
064 * consequences.
065 * </p>
066 * <p>
067 * To customize the JavaScript code used to submit the form we must override {@link #getTriggerJavaScript()}. 
068 * This can be helpful to implement additional client side behaviors like disabling the link during form submission.
069 * </p>
070 * 
071 * @author chris
072 * @author jcompagner
073 * @author Igor Vaynberg (ivaynberg)
074 * @author Eelco Hillenius
075 * 
076 */
077public class SubmitLink extends AbstractSubmitLink
078{
079        private static final long serialVersionUID = 1L;
080
081        /**
082         * With this constructor the SubmitLink must be inside a Form.
083         * 
084         * @param id
085         *            The id of the submitlink.
086         */
087        public SubmitLink(String id)
088        {
089                super(id);
090        }
091
092        /**
093         * With this constructor the SubmitLink will submit the {@link Form} that is given when the link
094         * is clicked on.
095         * 
096         * The SubmitLink doesn't have to be inside the {@link Form}. But currently if it is outside the
097         * {@link Form} and the SubmitLink is rendered first, then the {@link Form} will have a
098         * generated javascript/css id. The markup javascript/css id that can exist will be overridden.
099         * 
100         * @param id
101         *            The id of the submitlink.
102         * @param form
103         *            The form which this submitlink must submit.
104         */
105        public SubmitLink(String id, Form<?> form)
106        {
107                super(id, form);
108        }
109
110
111        /**
112         * With this constructor the SubmitLink must be inside a Form.
113         * 
114         * @param id
115         *            The id of the submitlink.
116         * @param model
117         *            The model for this submitlink, It won't be used by the submit link itself, but it
118         *            can be used for keeping state
119         */
120        public SubmitLink(String id, IModel<?> model)
121        {
122                super(id, model);
123        }
124
125        /**
126         * With this constructor the SubmitLink will submit the {@link Form} that is given when the link
127         * is clicked on.
128         * 
129         * The SubmitLink doesn't have to be in inside the {@link Form}. But currently if it is outside
130         * the {@link Form} and the SubmitLink will be rendered first. Then the {@link Form} will have a
131         * generated javascript/css id. The markup javascript/css id that can exist will be overridden.
132         * 
133         * @param id
134         *            The id of the submitlink.
135         * @param model
136         *            The model for this submitlink, It won't be used by the submit link itself, but it
137         *            can be used for keeping state
138         * @param form
139         *            The form which this submitlink must submit.
140         */
141        public SubmitLink(String id, IModel<?> model, Form<?> form)
142        {
143                super(id, model, form);
144        }
145
146        /**
147         * This method is here as a means to fall back on normal link behavior when this link is not
148         * nested in a form. Not intended to be called by clients directly.
149         * 
150         * @see IRequestListener#onRequest()
151         */
152        public final void onLinkClicked()
153        {
154                onSubmit();
155                onAfterSubmit();
156        }
157
158        @Override
159        protected void onComponentTag(ComponentTag tag)
160        {
161                super.onComponentTag(tag);
162
163                if (isEnabledInHierarchy())
164                {
165                        if (tag.getName().equalsIgnoreCase("a") || tag.getName().equalsIgnoreCase("link")
166                                || tag.getName().equalsIgnoreCase("area"))
167                        {
168                                tag.put("href", "#");
169                        }
170                        else if (tag.getName().equalsIgnoreCase("button"))
171                        {
172                                // WICKET-5597 prevent default submit
173                                tag.put("type", "button");
174                        }
175                }
176                else
177                {
178                        disableLink(tag);
179                }
180        }
181        
182        @Override
183        public void renderHead(IHeaderResponse response)
184        {
185                super.renderHead(response);
186
187                if (isEnabledInHierarchy())
188                {
189                        response.render(OnEventHeaderItem.forComponent(this, "click", getTriggerJavaScript()));
190                }
191        }
192
193        /**
194         * Controls whether or not clicking on this link will trigger a javascript submit event, firing
195         * any submit handler added to the form. True by default.
196         * 
197         * @return true if form's javascript submit handlers should be invoked, false otherwise
198         */
199        protected boolean shouldTriggerJavaScriptSubmitEvent()
200        {
201                return true;
202        }
203
204        /**
205         * The JavaScript which triggers this link. Method is non-final so that subclasses can decorate
206         * the provided script by wrapping their own JS around a call to super.getTriggerJavaScript().
207         * 
208         * @return The JavaScript to be executed when the link is clicked.
209         */
210        protected CharSequence getTriggerJavaScript()
211        {
212                if (getForm() != null)
213                {
214                        // find the root form - the one we are really going to submit
215                        Form<?> root = getForm().getRootForm();
216
217                        StringBuilder script = new StringBuilder();
218                        script.append(root.getJsForSubmitter(this, shouldTriggerJavaScriptSubmitEvent()));
219                        script.append("return false;");
220                        
221                        return script;
222                }
223                else
224                {
225                        return null;
226                }
227        }
228
229        @Override
230        public void onError()
231        {
232        }
233
234        /**
235         * Override this method to provide special submit handling in a multi-button form. This method
236         * will be called <em>after</em> the form's onSubmit method.
237         */
238        @Override
239        public void onAfterSubmit()
240        {
241        }
242
243        /**
244         * Override this method to provide special submit handling in a multi-button form. This method
245         * will be called <em>before</em> the form's onSubmit method.
246         */
247        @Override
248        public void onSubmit()
249        {
250        }
251}