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 }