001 // Copyright 2008, 2009, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.corelib.components;
016
017 import org.apache.tapestry5.*;
018 import org.apache.tapestry5.annotations.Environmental;
019 import org.apache.tapestry5.annotations.Events;
020 import org.apache.tapestry5.annotations.Parameter;
021 import org.apache.tapestry5.annotations.SupportsInformalParameters;
022 import org.apache.tapestry5.corelib.data.InsertPosition;
023 import org.apache.tapestry5.dom.Element;
024 import org.apache.tapestry5.internal.services.RequestConstants;
025 import org.apache.tapestry5.ioc.annotations.Inject;
026 import org.apache.tapestry5.json.JSONObject;
027 import org.apache.tapestry5.services.ClientBehaviorSupport;
028 import org.apache.tapestry5.services.FormSupport;
029 import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
030 import org.apache.tapestry5.services.ajax.JSONCallback;
031 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
032
033 import java.io.IOException;
034
035 /**
036 * A way to add new content to an existing Form. The FormInjector emulates its tag from the template (or uses a
037 * <div>). When triggered, new content is obtained from the application and is injected before or after the
038 * element.
039 * <p/>
040 * On the client side, a new function, trigger(), is added to the element. Invoking this client-side function will
041 * trigger the FormInjector; a request is sent to the server, new content is generated, and the new content is placed
042 * before or after (per configuration) the existing FormInjector element.
043 *
044 * @tapestrydoc
045 */
046 @SupportsInformalParameters
047 @Events(EventConstants.ACTION)
048 public class FormInjector implements ClientElement
049 {
050 public static final String INJECT_EVENT = "inject";
051
052 /**
053 * The context for the link (optional parameter). This list of values will be converted into strings and included in
054 * the URI. The strings will be coerced back to whatever their values are and made available to event handler
055 * methods.
056 */
057 @Parameter
058 private Object[] context;
059
060 @Parameter(defaultPrefix = BindingConstants.LITERAL,
061 value = BindingConstants.SYMBOL + ":" + ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION)
062 private InsertPosition position;
063
064 /**
065 * Name of a function on the client-side Tapestry.ElementEffect object that is invoked to make added content
066 * visible. The default value is "highlight".
067 */
068 @Parameter(defaultPrefix = BindingConstants.LITERAL,
069 value = BindingConstants.SYMBOL + ":" + ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION)
070 private String show;
071
072 /**
073 * The element name to render, which is normally the element name used to represent the FormInjector component in
074 * the template, or "div".
075 */
076 @Parameter(defaultPrefix = BindingConstants.LITERAL)
077 private String element;
078
079 @Environmental
080 private JavaScriptSupport javascriptSupport;
081
082 @Environmental
083 private FormSupport formSupport;
084
085 @Environmental
086 private ClientBehaviorSupport clientBehaviorSupport;
087
088 @SuppressWarnings("unchecked")
089 @Environmental
090 private TrackableComponentEventCallback eventCallback;
091
092 private String clientId;
093
094 @Inject
095 private ComponentResources resources;
096
097 @Inject
098 private AjaxResponseRenderer ajaxResponseRenderer;
099
100 private Element clientElement;
101
102 String defaultElement()
103 {
104 return resources.getElementName("div");
105 }
106
107 void beginRender(MarkupWriter writer)
108 {
109 clientId = javascriptSupport.allocateClientId(resources);
110
111 clientElement = writer.element(element, "id", clientId);
112
113 resources.renderInformalParameters(writer);
114
115 // Now work on the JavaScript side of things.
116
117 Link link = resources.createEventLink(INJECT_EVENT, context);
118
119 link.addParameter(RequestConstants.FORM_CLIENTID_PARAMETER, formSupport.getClientId());
120 link.addParameter(RequestConstants.FORM_COMPONENTID_PARAMETER, formSupport.getFormComponentId());
121
122 clientBehaviorSupport.addFormInjector(clientId, link, position, show);
123 }
124
125 void afterRender(MarkupWriter writer)
126 {
127 writer.end();
128
129 // Add the class name to the rendered client element. This allows nested elements to locate
130 // the containing FormInjector element.
131
132 clientElement.addClassName("t-forminjector");
133 }
134
135 /**
136 * Returns the unique client-side id of the rendered element.
137 */
138 public String getClientId()
139 {
140 return clientId;
141 }
142
143 /**
144 * Invoked via an Ajax request. Triggers an action event and captures the return value. The return value from the
145 * event notification is what will ultimately render (typically, its a Block).
146 */
147 void onInject(EventContext context) throws IOException
148 {
149 ajaxResponseRenderer.addCallback(new JSONCallback()
150 {
151 public void run(JSONObject reply)
152 {
153 clientId = javascriptSupport.allocateClientId(resources);
154
155 reply.put("elementId", clientId);
156 }
157 });
158
159 resources.triggerContextEvent(EventConstants.ACTION, context, eventCallback);
160 }
161 }