SpringBoot-VueJSadds VueJS to a Spring-boot Project for Creating Client-Side Application Logic Within Spring Controllers.
Insert the dependency in your pom.xmlfile:
<dependency>
<groupId>io.github.jeemv.springboot.vuejs</groupId>
<artifactId>springboot-vuejs</artifactId>
<version>[1.0,)</version>
</dependency>@Controller
@RequestMapping("/ui/")
public class UiTest {
@GetMapping("test")
public String index(ModelMap model) {
VueJS vue=new VueJS("#app");
vue.addData("message", "Hello world!");
model.put("vue", vue);
return "index";
}
}The index.html mustache view:
<div id="app">
<%message%>
<input v-model="message">
</div>
{{{vue}}}Mustache view use double mustache for variables (message in the example), so the VueJS instance is set by default to use <% and %> as delimiters.
The vue variable generates the javascript code for the view instance creation. The triple mustache {{{vue}}}is use for javascript/html code unescaping.
This technique has the advantage of providing a globale instance of VueJS for all the actions of a controller. Create a configuration class to allow the autowiring of VueJS:
@Configuration
@ComponentScan("io.github.jeemv.springboot.vuejs")
public class AppConfiguration {
}In your controller:
@Controller
@RequestMapping("/ui/")
public class UiTest {
@AutoWired
private VueJS vue;
@ModelAttribute(name = "vue")
private VueJS getVue() {
return this.vue;
}
@GetMapping("test")
public String index(ModelMap model) {
vue.addData("message", "Hello world!");
return "index";
}
}In this case, you can directly configure VueJS in the application.properties file:
springboot.vuejs.delimiters=<%,%>
springboot.vuejs.axios=true
springboot.vuejs.el=v-appFor a more punctual use, in a single method for example, It is possible to use the @ModelAttribute annotation :
@Controller
@RequestMapping("/ui/")
public class UiTest {
@ModelAttribute("vue")
public VueJS getVue() {
return new VueJS("#app");
}
@GetMapping("test")
public String index(@ModelAttribute("vue") VueJS vue) {
vue.addData("message", "Hello world!");
return "index";
}
}AOP loading in pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>AOP activation in app config file:
@Configuration
@ComponentScan("io.github.jeemv.springboot.vuejs.aspects")
@EnableAspectJAutoProxy
public class AppConfig {
}AOP usage in controller:
@Controller
@RequestMapping("/ui/")
public class UiTest {
@GetMapping("test")
@VueJSInstance
public String test2(VueJS vue,ModelMap model) {
vue.addData("message", "Hello world!");
return "index";
}
}Adds data object for the Vue instance.
vue.addData("visible",false);
vue.addData("group",group);//where group is an instance of the class Group
vue.addData("users",users);//where users is an ArrayList of UserAdds a method to the vue instance
vue.addMethod("toggleVisible", "this.visible=!this.visible;");
vue.addMethod("addScore","this.scores.push(score)","score");Adds a computed property to the vue instance
vue.addComputed("count", "return this.users.length");A computed property can have a setter:
vue.addMethod("fullname","return this.firstName + ' ' + this.lastName;","var names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[names.length - 1];");Adds a watcher on variable to the view instance
vue.addWatcher("value", "'value was '+oldValue+'. It is now '+val;");It is possible to create components at runtime, but it is more efficient to generate them before:
public class CompoButton {
public static void main (String[] args) throws java.lang.Exception {
VueComponent compo=new VueComponent("button-counter");
compo.addData("count", 0);
compo.setTemplate("<button @click=\"count++\">You clicked me {{ count }} times.</button>");
compo.createFile(false);
}
}The generated file is created in {project-folder}/src/main/resources/static/vueJS/button-counter.js
//Script generated with VueComponent at Thu Oct 11 03:01:09 CEST 2018
Vue.component('button-counter',{
"data":function() {
return {
"count":0
};
}
,"template":"<button @click=\"count++\">You clicked me {{ count }} times.</button>"
}
);Usage:
<script src="/vueJS/button-counter.js"></script>
...
<button-counter></button-counter>Templates are easier to create in a file:
Create the file /src/main/resources/templates/vueJS/button-counter.html
<button @click="count++">
You clicked me {{ count }} times.
</button>Modify the class CompoButton:
public class CompoButton {
public static void main (String[] args) throws java.lang.Exception {
VueComponent compo=new VueComponent("button-counter");
compo.addData("count", 0);
compo.setDefaultTemplateFile();
compo.createFile(false);
}
}the generated file is the same, but the method is more convenient.
Default delimiters are <% and %>.
For changing the plain text interpolation delimiters and avoid conflict with other template packages, you can modify them with:
vue.setDelimiters("{!","!}");You can also generate code to perform ajax queries:
vue.addMethod("submit",Http.postForm("formRef","console.log('submit datas!')"));HTTP calls from Vue.js to SpringBoot REST backend:
vue.addMethod("saveUser",Http.post("user/","user","console.log('User added!')"),"user");
vue.addMethod("updateUser",Http.put("user/","user","console.log('User updated!')"),"user");For axios, Do not forget to include the corresponding js file.
The javascript code is sometimes too large to be neatly integrated into a java controller. In this case, it can be delocalized in a javascript file, which can refer to java variables of the controller.
The java variables are parsed with ${varName} usage.
//resource/static/js/sample.js
console.log("${message}"); @GetMapping("sample")
public String testJs(@ModelAttribute("vue") VueJS vue) throws IOException {
JavascriptResource js = JavascriptResource.create("sample");
js.put("message", "Hello world!");
vue.addMethod("click", js.parseContent());
return "view";
}To avoid the multiplicity of javascript files, it is possible to group several scripts in the same file.
Each script (qualified as a module) must be identified in the javascript file by a comment on a single line bearing its name, and a comment marking the end (also mentioning the name of the script).
Each script can possibly be isolated, which is without consequences.
//resource/static/js/multi.js
//----------------consoleMsg-----------------------
console.log("${message}");
//----------------consoleMsg (end)-----------------
//----------------alertMsg-------------------------
(function(){
alert("${message}");
})();
//----------------alertMsg (end)------------------- @GetMapping("sample")
public String testJsMulti(@ModelAttribute("vue") VueJS vue) throws IOException {
JavascriptMultiModulesResource jsMulti=JavascriptMultiModulesResource.create("multi");
jsMulti.getModule("consoleMsg").put("message", "This is a console message");
vue.addMethod("click", js.parseContent("consoleMsg"));
return "view";
}