[ https://issues.apache.org/jira/browse/WICKET-6055?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15071011#comment-15071011 ]
ASF GitHub Bot commented on WICKET-6055: ---------------------------------------- Github user martin-g commented on a diff in the pull request: https://github.com/apache/wicket/pull/151#discussion_r48414689 --- Diff: wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java --- @@ -139,56 +182,176 @@ protected void handleCallbackScript(final IHeaderResponse response, @Override protected void onBeforeRender() { - if (state == 0) + if (state == LoadingState.WAITING_FOR_LAZY_COMPONENT) { add(getLoadingComponent(LAZY_LOAD_COMPONENT_ID)); - setState((byte)1); + setState(LoadingState.REPLACING_LAZY_COMPONENT); } super.onBeforeRender(); } + @Override + public void onEvent(IEvent<?> event) + { + super.onEvent(event); + + if (state == LoadingState.REPLACING_LAZY_COMPONENT) + { + if (isReadyForReplacement()) + { + // create the lazy load component + Component component = getLazyLoadComponent(LAZY_LOAD_COMPONENT_ID); + + // replace the spinner with the new component + AjaxLazyLoadPanel.this.replace(component); + + // mark replacement as done + setState(LoadingState.REPLACEMENT_COMPLETED); + + // remove ourselves from the timer + getLazyLoadTimer().removeLazyLoadPanel(this); + + AjaxRequestTarget target = getRequestCycle().find(AjaxRequestTarget.class); + + // notify our subclasses of the updated component + onComponentLoaded(component, target); + + // repaint our selves if there's an AJAX request in play, otherwise let the page + // redraw itself + if (target != null) + { + target.add(AjaxLazyLoadPanel.this); + } + } + } + } + + @Override + protected void onRemove() + { + super.onRemove(); + + // mark replacement as done + setState(LoadingState.REPLACEMENT_COMPLETED); + + // remove ourselves from the timer + AjaxLazyLoadTimer timer = getLazyLoadTimer(); + if (timer != null) + { + timer.removeLazyLoadPanel(this); + } + } + /** + * Sets the new LoadingState for this panel. * - * @param state + * @param newState */ - private void setState(final byte state) + private void setState(final LoadingState newState) { - this.state = state; + this.state = newState; getPage().dirty(); } /** + * Gets the central lazy load timer from the page if one is registered * - * @param markupId - * The components markupid. - * @return The component that must be lazy created. You may call setRenderBodyOnly(true) on this - * component if you need the body only. + * @return the central lazy load timer or {@code null} */ - public abstract Component getLazyLoadComponent(String markupId); + private AjaxLazyLoadTimer getLazyLoadTimer() + { + return getPage().getMetaData(AJAX_LAZY_LOAD_TIMER); + } /** - * Called when the placeholder component is replaced with the lazy loaded one. - * - * @param component - * The lazy loaded component - * @param target - * The Ajax request handler + * Sets the central lazy load timer on the page + * + * @param timer + * the timer to set */ - protected void onComponentLoaded(Component component, AjaxRequestTarget target) + private void setLazyLoadTimer(AjaxLazyLoadTimer timer) { + getPage().setMetaData(AJAX_LAZY_LOAD_TIMER, timer); } /** - * @param markupId - * The components markupid. - * @return The component to show while the real component is being created. + * @deprecated No longer called as the AjaxLazyLoadPanel uses a central AJAX timer, and it + * doesn't make sense to modify the single timer from multiple panels */ - public Component getLoadingComponent(final String markupId) + @SuppressWarnings("javadoc") + @Deprecated + protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { - IRequestHandler handler = new ResourceReferenceRequestHandler( - AbstractDefaultAjaxBehavior.INDICATOR); - return new Label(markupId, "<img alt=\"Loading...\" src=\"" + - RequestCycle.get().urlFor(handler) + "\"/>").setEscapeModelStrings(false); } + /** + * Allows subclasses to change the callback script if needed. + * + * @param response + * the current response that writes to the header + * @param callbackScript + * the JavaScript to write in the header + * @param component + * the component which produced the callback script + */ + protected void handleCallbackScript(final IHeaderResponse response, + final CharSequence callbackScript, final Component component) + { + response.render(OnDomReadyHeaderItem.forScript(callbackScript)); + } + + /** + * The AJAX timer for updating the AjaxLazyLoadPanel. Is designed to be a page-local singleton + * keeping track of multiple LazyLoadPanels using reference counting. + */ + private static class AjaxLazyLoadTimer extends AbstractAjaxTimerBehavior + { + private static final long serialVersionUID = 1L; + + private final List<AjaxLazyLoadPanel> lazyLoadPanels = new ArrayList<>(); + + public AjaxLazyLoadTimer() + { + super(Duration.ONE_SECOND); --- End diff -- +1 to make the duration configurable, e.g. overridable method in AjaxLazyLoadPanel. > AjaxLazyLoadPanel should provide non-blocking lazy load > ------------------------------------------------------- > > Key: WICKET-6055 > URL: https://issues.apache.org/jira/browse/WICKET-6055 > Project: Wicket > Issue Type: Improvement > Components: wicket-extensions > Affects Versions: 7.1.0 > Reporter: Martijn Dashorst > Assignee: Martijn Dashorst > > When having multiple AjaxLazyLoadPanels on your page, they all block their > Wicket request thread until the content is ready to load. This can be > problematic when you try to wait for some background job to finish and want > to poll for that job to be ready, and only then update the contents. > The improvement would be to add a method that gives the developer the option > to not update just yet (isReadyForReplacement) and when it returns true, > start the replacement. By default this would return true, implementing the > current behavior of the AjaxLazyLoadPanel. > Furthermore to improve the responsiveness of the ALLP it should add a single > timer to the page that can be used by multiple ALLPs to update themselves. > The timer would poll each second and the ALLPs would use Wicket's event bus > to update themselves. With some reference counting, the timer can remove > itself from the page when all ALLPs have updated themselves. > This enables refreshing the page as well when outside an AJAX context, or > having a user be impatient and pressing F5. -- This message was sent by Atlassian JIRA (v6.3.4#6332)