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.ajax; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.wicket.Component; 023import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; 024import org.apache.wicket.markup.head.IHeaderResponse; 025import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 026import org.apache.wicket.util.lang.Args; 027import org.apache.wicket.util.lang.Checks; 028import org.apache.wicket.util.string.Strings; 029import org.danekja.java.util.function.serializable.SerializableConsumer; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * An ajax behavior that is attached to a certain client-side (usually javascript) event, such as 035 * click, change, keydown, etc. 036 * <p> 037 * Example: 038 * 039 * <pre> 040 * WebMarkupContainer div=new WebMarkupContainer(...); 041 * div.setOutputMarkupId(true); 042 * div.add(new AjaxEventBehavior("click") { 043 * protected void onEvent(AjaxRequestTarget target) { 044 * System.out.println("ajax here!"); 045 * } 046 * } 047 * </pre> 048 * 049 * This behavior will be linked to the <em>click</em> javascript event of the div WebMarkupContainer 050 * represents, and so anytime a user clicks this div the {@link #onEvent(AjaxRequestTarget)} of the 051 * behavior is invoked. 052 * 053 * <p> 054 * <strong>Note</strong>: {@link #getEvent()} method cuts any <em>on</em> prefix from the given event name(s). 055 * This is being done for easier migration of applications coming from Wicket 1.5.x where Wicket used 056 * inline attributes like 'onclick=...'. If the application needs to use custom events with names starting with 057 * <em>on</em> then {@link #getEvent()} should be overridden. 058 * </p> 059 * 060 * @since 1.2 061 * 062 * @author Igor Vaynberg (ivaynberg) 063 * @see #onEvent(AjaxRequestTarget) 064 */ 065public abstract class AjaxEventBehavior extends AbstractDefaultAjaxBehavior 066{ 067 private static final Logger LOGGER = LoggerFactory.getLogger(AjaxEventBehavior.class); 068 069 private static final long serialVersionUID = 1L; 070 071 private static final char EVENT_NAME_SEPARATOR = ' '; 072 073 private final String event; 074 075 /** 076 * Construct. 077 * 078 * @param event 079 * the event this behavior will be attached to 080 */ 081 public AjaxEventBehavior(String event) 082 { 083 Args.notEmpty(event, "event"); 084 085 if ("inputchange".equals(event)) 086 { 087 // TODO Wicket 10 remove (see WICKET-6667) 088 event = "input"; 089 LOGGER.warn("Since version 9.0.0 Wicket no longer supports 'inputchange' events, please use 'input' instead"); 090 } 091 092 this.event = event; 093 } 094 095 @Override 096 public void renderHead(final Component component, final IHeaderResponse response) 097 { 098 super.renderHead(component, response); 099 100 if (component.isEnabledInHierarchy()) 101 { 102 CharSequence js = getCallbackScript(component); 103 104 response.render(OnDomReadyHeaderItem.forScript(js.toString())); 105 } 106 } 107 108 @Override 109 protected void updateAjaxAttributes(AjaxRequestAttributes attributes) 110 { 111 super.updateAjaxAttributes(attributes); 112 113 String evt = getEvent(); 114 Checks.notEmpty(evt, "getEvent() should return non-empty event name(s)"); 115 attributes.setEventNames(evt); 116 } 117 118 /** 119 * @return event 120 * the event this behavior is attached to 121 */ 122 public String getEvent() 123 { 124 if (event.indexOf(EVENT_NAME_SEPARATOR) == -1) 125 { 126 return event; 127 } 128 129 String[] splitEvents = Strings.split(event, EVENT_NAME_SEPARATOR); 130 List<String> cleanedEvents = new ArrayList<>(splitEvents.length); 131 for (String evt : splitEvents) 132 { 133 evt = evt.trim(); 134 if (!Strings.isEmpty(evt)) 135 { 136 cleanedEvents.add(evt); 137 } 138 } 139 140 return Strings.join(" ", cleanedEvents); 141 } 142 143 @Override 144 protected final void respond(final AjaxRequestTarget target) 145 { 146 onEvent(target); 147 } 148 149 /** 150 * Listener method for the ajax event 151 * 152 * @param target 153 * the current request handler 154 */ 155 protected abstract void onEvent(final AjaxRequestTarget target); 156 157 /** 158 * Creates an {@link AjaxEventBehavior} based on lambda expressions 159 * 160 * @param eventName 161 * the event name 162 * @param onEvent 163 * the {@code SerializableConsumer} which accepts the {@link AjaxRequestTarget} 164 * @return the {@link AjaxEventBehavior} 165 */ 166 public static AjaxEventBehavior onEvent(String eventName, SerializableConsumer<AjaxRequestTarget> onEvent) 167 { 168 Args.notNull(onEvent, "onEvent"); 169 170 return new AjaxEventBehavior(eventName) 171 { 172 private static final long serialVersionUID = 1L; 173 174 @Override 175 protected void onEvent(AjaxRequestTarget target) 176 { 177 onEvent.accept(target); 178 } 179 }; 180 } 181}