mirror of
https://github.com/Febbweiss/springboot-react-webpack.git
synced 2026-03-04 14:15:36 +00:00
Feature: React server side rendering
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
package com.opengroupe.cloud.saas.domain;
|
||||
|
||||
import groovy.transform.ToString;
|
||||
|
||||
@ToString
|
||||
public class Comment {
|
||||
|
||||
private final Long id;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.opengroupe.cloud.saas.rest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@@ -10,12 +10,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.opengroupe.cloud.saas.domain.Comment;
|
||||
import com.opengroupe.cloud.saas.service.CommentService;
|
||||
|
||||
@RestController
|
||||
public class CommentController {
|
||||
|
||||
private List<Comment> comments = new ArrayList<Comment>();
|
||||
|
||||
@Autowired
|
||||
private CommentService service;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String index() {
|
||||
return "Greetings from Spring Boot!";
|
||||
@@ -23,7 +25,7 @@ public class CommentController {
|
||||
|
||||
@RequestMapping(value="/api/comments", method=RequestMethod.GET)
|
||||
public @ResponseBody List<Comment> comments() {
|
||||
return comments;
|
||||
return service.getAll();
|
||||
}
|
||||
|
||||
@RequestMapping(value="/api/comments", method=RequestMethod.POST)
|
||||
@@ -31,7 +33,8 @@ public class CommentController {
|
||||
@RequestParam(value="id", required=true) Long id,
|
||||
@RequestParam(value="author", required=true) String author,
|
||||
@RequestParam(value="text", required=true) String text) {
|
||||
comments.add(new Comment(id, author, text));
|
||||
return comments;
|
||||
service.add(new Comment(id, author, text));
|
||||
return service.getAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.opengroupe.cloud.saas.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.opengroupe.cloud.saas.domain.Comment;
|
||||
|
||||
@Component
|
||||
public class CommentService {
|
||||
|
||||
private List<Comment> comments = new ArrayList<Comment>();
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
comments.addAll(Arrays.asList(new Comment(1L, "Pete Hunt", "This is one comment"),
|
||||
new Comment(2L, "Jordan Walke", "This is *another* comment")));
|
||||
}
|
||||
|
||||
public void add(Comment comment) {
|
||||
comments.add(comment);
|
||||
}
|
||||
|
||||
public List<Comment> getAll() {
|
||||
return comments;
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/opengroupe/cloud/saas/util/React.java
Normal file
50
src/main/java/com/opengroupe/cloud/saas/util/React.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.opengroupe.cloud.saas.util;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import com.opengroupe.cloud.saas.domain.Comment;
|
||||
|
||||
import jdk.nashorn.api.scripting.NashornScriptEngine;
|
||||
|
||||
public class React {
|
||||
|
||||
private ThreadLocal<NashornScriptEngine> engineHolder = new ThreadLocal<NashornScriptEngine>() {
|
||||
@Override
|
||||
protected NashornScriptEngine initialValue() {
|
||||
NashornScriptEngine nashornScriptEngine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
|
||||
try {
|
||||
nashornScriptEngine.eval(read("static/js/nashorn-polyfill.js"));
|
||||
nashornScriptEngine.eval(read("META-INF/resources/webjars/react/0.14.7/react.min.js"));
|
||||
nashornScriptEngine.eval(read("META-INF/resources/webjars/marked/0.3.2/marked.js"));
|
||||
// nashornScriptEngine.eval(read("classpath:static/js/react-bootstrap.js"));
|
||||
// nashornScriptEngine.eval(read("classpath:static/js/comments.js"));
|
||||
nashornScriptEngine.eval(read("static/js/app.js"));
|
||||
nashornScriptEngine.eval(read("static/js/app.render.js"));
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return nashornScriptEngine;
|
||||
}
|
||||
};
|
||||
|
||||
public String renderCommentBox(List<Comment> comments) {
|
||||
try {
|
||||
Object html = engineHolder.get().invokeFunction("renderServer", comments);
|
||||
return String.valueOf(html);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("failed to render react component", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Reader read(String path) {
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream(path);
|
||||
return new InputStreamReader(in);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,31 @@
|
||||
package com.opengroupe.cloud.saas.web;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.opengroupe.cloud.saas.domain.Comment;
|
||||
import com.opengroupe.cloud.saas.service.CommentService;
|
||||
import com.opengroupe.cloud.saas.util.React;
|
||||
|
||||
@Controller
|
||||
public class ViewController {
|
||||
|
||||
@Autowired
|
||||
private CommentService service;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private React react = new React();
|
||||
|
||||
@RequestMapping("/greeting")
|
||||
public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
|
||||
model.addAttribute("name", name);
|
||||
@@ -15,7 +33,12 @@ public class ViewController {
|
||||
}
|
||||
|
||||
@RequestMapping("/index")
|
||||
public String index(Model model) {
|
||||
public String index(Model model) throws JsonProcessingException {
|
||||
List<Comment> comments = service.getAll();
|
||||
String commentBox = react.renderCommentBox(comments);
|
||||
String data = objectMapper.writeValueAsString(comments);
|
||||
model.addAttribute("markup", commentBox);
|
||||
model.addAttribute("data", data);
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ var CommentForm = React.createClass({
|
||||
});
|
||||
var CommentBox = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {data: []};
|
||||
return {data: this.props.data || []};
|
||||
},
|
||||
loadCommentsFromServer: function() {
|
||||
$.ajax({
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
ReactDOM.render(
|
||||
<CommentBox url="/api/comments" pollInterval={2000}/>,
|
||||
document.getElementById('content')
|
||||
);
|
||||
var renderClient = function (comments) {
|
||||
var data = comments || [];
|
||||
ReactDOM.render(
|
||||
<CommentBox data={data} url="/api/comments" pollInterval={2000}/>,
|
||||
document.getElementById('content')
|
||||
);
|
||||
};
|
||||
|
||||
var renderServer = function (comments) {
|
||||
var data = Java.from(comments);
|
||||
return React.renderToString(
|
||||
<CommentBox data={data} url="/api/comments" pollInterval={2000} />
|
||||
);
|
||||
};
|
||||
6
src/main/resources/static/js/nashorn-polyfill.js
Normal file
6
src/main/resources/static/js/nashorn-polyfill.js
Normal file
@@ -0,0 +1,6 @@
|
||||
var global = this;
|
||||
|
||||
var console = {};
|
||||
console.debug = print;
|
||||
console.warn = print;
|
||||
console.log = print;
|
||||
@@ -11,7 +11,7 @@
|
||||
<div th:replace="fragments/header"></div>
|
||||
|
||||
<div class="container">
|
||||
<div id="content"></div>
|
||||
<div id="content" th:utext="${markup}"></div>
|
||||
</div>
|
||||
|
||||
<div th:replace="fragments/footer"></div>
|
||||
@@ -20,6 +20,9 @@
|
||||
<script src="/js/comments.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
<script src="/js/app.render.js"></script>
|
||||
<script src="/js/test.js"></script>
|
||||
<script th:inline="javascript">
|
||||
var initialData = JSON.parse(/*[[${data}]]*/ '[]');
|
||||
renderClient(initialData);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user