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.ajax.markup.html.repeater;
018
019import java.util.EmptyStackException;
020
021import org.apache.wicket.Component;
022import org.apache.wicket.ajax.AjaxRequestTarget;
023import org.apache.wicket.core.util.string.JavaScriptUtils;
024import org.apache.wicket.markup.ComponentTag;
025import org.apache.wicket.markup.IMarkupFragment;
026import org.apache.wicket.markup.MarkupStream;
027import org.apache.wicket.markup.html.WebMarkupContainer;
028import org.apache.wicket.markup.html.panel.Panel;
029import org.apache.wicket.markup.parser.XmlTag.TagType;
030import org.apache.wicket.markup.repeater.RepeatingView;
031
032/**
033 * An panel for an <it>Ajax-ified</it> list of components.
034 * <p>
035 * Allows to dynamically append and delete components without an update of a whole container.
036 * 
037 * @see #append(Component, AjaxRequestTarget)
038 * @see #delete(Component, AjaxRequestTarget)
039 * 
040 * @author svenmeier
041 */
042public class AjaxListPanel extends Panel
043{
044
045        private static final long serialVersionUID = 1L;
046        
047        private WebMarkupContainer container;
048
049        private RepeatingView repeater;
050
051        public AjaxListPanel(String id)
052        {
053                super(id);
054
055                this.container = new WebMarkupContainer("container");
056                this.container.setOutputMarkupId(true);
057                add(this.container);
058
059                this.repeater = new RepeatingView("repeater");
060                this.container.add(this.repeater);
061        }
062
063        /**
064         * Get an id for a new child to be appended.
065         * 
066         * @return id
067         * 
068         * @see #append(Component, AjaxRequestTarget)
069         */
070        public String newChildId() {
071                return repeater.newChildId();
072        }
073        
074        /**
075         * Append a component.
076         * 
077         * @param component
078         *            the component
079         * @param target
080         *            optional target
081         * @return the component
082         * 
083         * @param <T> component type
084         */
085        public <T extends Component> T append(T component, AjaxRequestTarget target)
086        {
087                this.repeater.add(component);
088                
089                if (target != null)
090                {
091                        IMarkupFragment markup = repeater.getMarkup();
092                        
093                        // append markup to be updated
094                        MarkupStream stream = new MarkupStream(markup);
095                        ComponentTag tag = stream.getTag().mutable();
096                        tag.getXmlTag().setType(TagType.OPEN_CLOSE);
097                        tag.getXmlTag().put("id", component.getMarkupId());
098
099                        target.prependJavaScript(String.format("Wicket.DOM.add(Wicket.DOM.get('%s'), '%s');",
100                                container.getMarkupId(), JavaScriptUtils.escapeQuotes(tag.toString())));
101                        
102                        // ... then update the appended component 
103                        target.add(component);
104                }
105
106                return component;
107        }
108        
109        /**
110         * Delete a component.
111         * 
112         * @param target
113         *            optional target
114         * @return the component
115         * @throws EmptyStackException if empty
116         * 
117         * @param <T> component type
118         */
119        public <T extends Component> T delete(T component, AjaxRequestTarget target) {
120
121                this.repeater.remove(component);
122                if (target != null)
123                {
124                        target.appendJavaScript(String.format("Wicket.DOM.remove(Wicket.DOM.get('%s'));", component.getMarkupId()));
125                }
126                
127                return (T)component;
128        }
129}