Skip to content

Commit 6b958aa

Browse files
committed
Add Thymeleaf ViewResolver for Web Flow
The classes are copied from thymeleaf-spring5, temporarily here until they become available in thymeleaf-spring6.
1 parent 6f82ff3 commit 6b958aa

File tree

4 files changed

+511
-0
lines changed

4 files changed

+511
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* =============================================================================
3+
*
4+
* Copyright (c) 2011-2018, The THYMELEAF team (http://www.thymeleaf.org)
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* =============================================================================
19+
*/
20+
package org.thymeleaf.spring6.view;
21+
22+
import org.springframework.web.servlet.View;
23+
import org.springframework.webflow.context.servlet.AjaxHandler;
24+
25+
26+
// Copied from thymeleaf-spring5.
27+
// Temporarily here until available in thymeleaf-spring6.
28+
29+
30+
/**
31+
* <p>
32+
* Interface defining getter and setter methods for an
33+
* {@code ajaxHandler} property in Views, so that they can
34+
* be used in Spring AJAX environments.
35+
* </p>
36+
*
37+
* @author Daniel Fern&aacute;ndez
38+
*
39+
* @since 3.0.3
40+
*
41+
*/
42+
public interface AjaxEnabledView extends View {
43+
44+
45+
46+
/**
47+
* <p>
48+
* Return the AJAX handler (from Spring Javascript) used
49+
* to determine whether a request is an AJAX request or not.
50+
* </p>
51+
* <p>
52+
* Views implementing this interface should be used with an instance of
53+
* {@link AjaxThymeleafViewResolver} or any of its subclasses,
54+
* so that {@link #setAjaxHandler(AjaxHandler)} can be called by
55+
* the resolver when resolving the view, setting the default
56+
* AJAX handler being used.
57+
* </p>
58+
*
59+
* @return the AJAX handler.
60+
*/
61+
public AjaxHandler getAjaxHandler();
62+
63+
64+
/**
65+
* <p>
66+
* Sets the AJAX handler (from Spring Javascript) used
67+
* to determine whether a request is an AJAX request or not.
68+
* </p>
69+
* <p>
70+
* Views implementing this interface should be used with an instance of
71+
* {@link AjaxThymeleafViewResolver} or any of its subclasses,
72+
* so that this method can be called by
73+
* the resolver when resolving the view, setting the default
74+
* AJAX handler being used.
75+
* </p>
76+
*
77+
* @param ajaxHandler the AJAX handler.
78+
*/
79+
public void setAjaxHandler(final AjaxHandler ajaxHandler);
80+
81+
82+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* =============================================================================
3+
*
4+
* Copyright (c) 2011-2018, The THYMELEAF team (http://www.thymeleaf.org)
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
* =============================================================================
19+
*/
20+
package org.thymeleaf.spring6.view;
21+
22+
import java.util.Arrays;
23+
import java.util.Collections;
24+
import java.util.HashSet;
25+
import java.util.Map;
26+
import java.util.Set;
27+
28+
import jakarta.servlet.http.HttpServletRequest;
29+
import jakarta.servlet.http.HttpServletResponse;
30+
31+
import org.slf4j.Logger;
32+
import org.slf4j.LoggerFactory;
33+
import org.springframework.util.StringUtils;
34+
import org.springframework.webflow.context.servlet.AjaxHandler;
35+
import org.thymeleaf.exceptions.ConfigurationException;
36+
37+
38+
// Copied from thymeleaf-spring5.
39+
// Temporarily here until available in thymeleaf-spring6.
40+
41+
42+
/**
43+
* <p>
44+
* Subclass of {@link ThymeleafView} adding compatibility with AJAX events in
45+
* Spring JavaScript (part of Spring WebFlow). This allows this View implementation
46+
* to be able to return only <i>fragments</i> of the page.
47+
* </p>
48+
* <p>
49+
* These rendering of fragments is used, for example, in Spring WebFlow's &lt;render&gt;
50+
* instructions (though not only).
51+
* </p>
52+
* <p>
53+
* This view searches for a comma-separated list of <i>markup selectors</i> in a request
54+
* parameter called {@code fragments}.
55+
* </p>
56+
*
57+
* @author Daniel Fern&aacute;ndez
58+
*
59+
* @since 3.0.3
60+
*
61+
*/
62+
public class AjaxThymeleafView extends ThymeleafView implements AjaxEnabledView {
63+
64+
private static final Logger vlogger = LoggerFactory.getLogger(AjaxThymeleafView.class);
65+
66+
private static final String FRAGMENTS_PARAM = "fragments";
67+
68+
69+
private AjaxHandler ajaxHandler = null;
70+
71+
72+
73+
public AjaxThymeleafView() {
74+
super();
75+
}
76+
77+
78+
79+
public AjaxHandler getAjaxHandler() {
80+
return this.ajaxHandler;
81+
}
82+
83+
84+
public void setAjaxHandler(final AjaxHandler ajaxHandler) {
85+
this.ajaxHandler = ajaxHandler;
86+
}
87+
88+
89+
90+
91+
@Override
92+
public void render(final Map<String, ?> model, final HttpServletRequest request, final HttpServletResponse response)
93+
throws Exception {
94+
95+
96+
final AjaxHandler templateAjaxHandler = getAjaxHandler();
97+
98+
if (templateAjaxHandler == null) {
99+
throw new ConfigurationException("[THYMELEAF] AJAX Handler set into " +
100+
AjaxThymeleafView.class.getSimpleName() + " instance for template " +
101+
getTemplateName() + " is null.");
102+
}
103+
104+
if (templateAjaxHandler.isAjaxRequest(request, response)) {
105+
106+
final Set<String> fragmentsToRender = getRenderFragments(model, request, response);
107+
if (fragmentsToRender == null || fragmentsToRender.size() == 0) {
108+
vlogger.warn("[THYMELEAF] An Ajax request was detected, but no fragments were specified to be re-rendered. "
109+
+ "Falling back to full page render. This can cause unpredictable results when processing "
110+
+ "the ajax response on the client.");
111+
super.render(model, request, response);
112+
return;
113+
}
114+
115+
super.renderFragment(fragmentsToRender, model, request, response);
116+
117+
} else {
118+
119+
super.render(model, request, response);
120+
121+
}
122+
123+
}
124+
125+
126+
127+
128+
@SuppressWarnings({ "rawtypes", "unused" })
129+
protected Set<String> getRenderFragments(
130+
final Map model, final HttpServletRequest request, final HttpServletResponse response) {
131+
final String fragmentsParam = request.getParameter(FRAGMENTS_PARAM);
132+
final String[] renderFragments = StringUtils.commaDelimitedListToStringArray(fragmentsParam);
133+
if (renderFragments.length == 0) {
134+
return null;
135+
}
136+
if (renderFragments.length == 1) {
137+
return Collections.singleton(renderFragments[0]);
138+
}
139+
return new HashSet<String>(Arrays.asList(renderFragments));
140+
}
141+
142+
143+
144+
}

0 commit comments

Comments
 (0)