Sunday, October 24, 2021

Kendo Wizard With HTTP POST Example

This article is an example of a Kendo Wizard Component sending an HTTP POST to a Spring Boot backend. The framework used is Kendo UI for jQuery. In this example, we send the entire Wizard form data after all the questions are answered.

The IDE used is Spring Tool Suite 4 and it is expected that the reader know a thing or two about Spring Boot. This is about Kendo Wizard so there won't be any extensive Spring Boot explanations. Make sure you are connected to the Internet because the code pulls the Kendo library from the Telerik CDN. Take note that this is just a demonstration code and you'll need to purchase a license if you are going to use Kendo UI for jQuery for more than just evaluation purposes.

First on the agenda is our controllers. Here is the WizardController class.


package com.blogspot.jpllosa.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import com.blogspot.jpllosa.model.Questionaire;

@Controller
public class WizardController {

	@GetMapping("/wizard")
	public String wizardForm() {
		return "wizard";
	}
	
	@GetMapping("/success")
	public String successPage() {
		return "success";
	}
	
	@PostMapping("/wizard")
	public ResponseEntity wizardSubmit(@ModelAttribute Questionaire questionaire) {
		System.out.println(questionaire);
		return new ResponseEntity<>("", 
				HttpStatus.OK);
	}
}

This class handles a couple of HTTP GET requests and an HTTP POST request. GET requests to /wizard and /success resources return the wizard.html and success.html templates respectively.

The POST request to the /wizard resource is handled by the wizardSubmit method. Spring is kind enough to map the Form data received to the Questionaire class. When a POST request is received, this prints the questionaire contents to the console and reponds with an HTTP OK.

Here's the Questionaire class.


package com.blogspot.jpllosa.model;

public class Questionaire {
	private String section1answer1;
	private String section1answer2;
	private String section2answer1;
	private String section2answer2;
	
	public String getSection1answer1() {
		return section1answer1;
	}
	public void setSection1answer1(String section1answer1) {
		this.section1answer1 = section1answer1;
	}
	public String getSection1answer2() {
		return section1answer2;
	}
	public void setSection1answer2(String section1answer2) {
		this.section1answer2 = section1answer2;
	}
	public String getSection2answer1() {
		return section2answer1;
	}
	public void setSection2answer1(String section2answer1) {
		this.section2answer1 = section2answer1;
	}
	public String getSection2answer2() {
		return section2answer2;
	}
	public void setSection2answer2(String section2answer2) {
		this.section2answer2 = section2answer2;
	}
	@Override
	public String toString() {
		return "Questionaire [section1answer1=" + section1answer1 + ", section1answer2=" + section1answer2
				+ ", section2answer1=" + section2answer1 + ", section2answer2=" + section2answer2 + "]";
	}
}

Here is the meat of this example. The Kendo Wizard has a couple of sections, each with a couple of questions. After answering all the questions, clicking on done triggers the done event handler. What this function does is grab all the form data and put it into a FormData object. The FormData is then sent to the /wizard endpopint via an AJAX request. If successful, the frontend code handles the POST-Redirect-GET because the backend can't do a redirect from an AJAX request. The backend just prints what was received in this example.


<!--https://jpllosa.blogspot.com-->
<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head> 
    <title>Kendo Wizard POST to Spring Boot Example</title>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"></meta>
    
    <link href="https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.default-v2.min.css" rel="stylesheet"></link>

    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://kendo.cdn.telerik.com/2021.1.224/js/kendo.all.min.js"></script>
</head>
<body>
	<div id="wizard"></div>

<script>
    var $wizard = $("#wizard");
    var wizard = $wizard.kendoWizard({
        validateForms: true,
        steps: [{
            title: "Sample Questionaire",
            content: "Click <b>Next</b> to begin.",
        },{
            title: "Section 1",
            form: {
                formData: {
                    section1answer1: "",
                    section1answer2: "",
                },
                items: [{
                    field: "section1answer1",
                    label: "Some silly question 1:",
                    validation: {
                        required: true,
                    },
                },{
                    field: "section1answer2",
                    label: "Some silly question 2:",
                    validation: {
                        required: true,
                    },
                }],
            },
        },{
            title: "Section 2",
            form: {
                formData: {
                    section2answer1: "",
                    section2answer2: "",
                },
                items: [{
                    field: "section2answer1",
                    label: "Another silly question 1:",
                    validation: {
                        required: true,
                    },
                },{
                    field: "section2answer2",
                    label: "Another silly question 2:",
                    validation: {
                        required: true,
                    },
                }],
            },
        },{
            title: "Done",
            content: "Thank you for answering.",
        }],
        done: function(e) {
        	var forms = e.forms;
        	var formData = new FormData();
        	for (let a = 0; a < forms.length; a++) {
        		var form = forms[a];
        		for (let b = 0; b < form._fields.length; b++) {
        			var field = form._fields[b];
        			formData.append(field.field, form._model[field.field]);
        		}
        	}

			$.ajax({
				type: "POST",
				url: "/wizard",
				contentType: false, // do not send content type header
				processData: false, // do not transform into a query string
				data: formData,
			})
			.done(function(data, textStatus, jqXhr) {
				// success = redirect to success page
				if (textStatus === 'success') {
					// post redirect get
					// back-end can't redirect from ajax request
					window.location.replace("/success");
				}
			})
			.fail(function(jqXhr, textStatus, errorThrown) {
				console.log("fail");
			})
			.always(function() {
				console.log("always");
			});
        }
    }).data('kendoWizard');
</script>
<style>
    .k-stepper .k-step-label .k-step-text {
        max-width: calc(30em - 20px);
    }
</style>
</body>
</html>

Here is how it would look like:

Kendo Wizard With HTTP POST Example
Kendo Wizard Start
Kendo Wizard With HTTP POST Example
Kendo Wizard Questions
Kendo Wizard With HTTP POST Example
Kendo Wizard Done
Kendo Wizard With HTTP POST Example
Kendo Wizard Success

Spring Boot should print something like below:


2021-10-23 21:05:25.845 INFO 5872 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet Completed initialization in 1 ms
Questionaire [section1answer1=silly 1, section1answer2=silly 2, section2answer1=another 1, section2answer2=another 2]

There you have it. The complete project can be cloned from github.com/jpllosa/spring-boot-kendo-wizard.


No comments:

Post a Comment