Sunday, October 31, 2021

Kendo DropDownList Hide Selected Option Example

This article is another example of the Kendo DropDownList Component. The framework used is Kendo UI for jQuery. This example stems from the StackOverflow question which I answered.

The original poster wanted to hide the selected option in the drop down list. For example, we have 5 items on the list. Namely, apples, mangoes, grapes, papaya, and banana. When a user selects apples, the drop down list will only show mangoes, grapes, papaya, and banana. The OP's idea was to remove the item from the data source. In our solution, we just hide whatever is selected. There is no need to remove the item from the data source. This idea is based on a previous article I have written, Kendo DropDownList Example. So on the open, we get what was selected and then hide. Simples.

Here is our solution. You can edit the code below using any text editor but I prefer Visual Studio Code. 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.

Here is how it would look like:

Kendo DropDownList Hide Selected Option Example
Kendo DropDownList Example

There you have it. A simple example on how to hide a selected option from the list using the Kendo DropDownList component.

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.


Monday, October 11, 2021

Kendo Grid Persist Row Selection Example

This article is an example of a Kendo Grid row selection issue. The framework used is Kendo UI for jQuery. Let's begin.

In the persist row selection in this knowledge base article, https://docs.telerik.com/kendo-ui/knowledge-base/persist-row-selection-while-paging, it chooses the wrong rows after choosing the page and going back then Shift+Click a row. Below is the code from the knowledge base article.

Follow these steps to reproduce the row selection issue.

  1. On page 1, select Brazil (row 3). The Brazil row should be selected.
  2. Go to page 2 then back to page 1. Brazil is still selected. Well and good.
  3. Shift+Click Belgium, the selection now starts from row 1 to row 5 (France, Germany, Brazil, France, Belgium). Selected should be row 3 to 5 (Brazil, France, Belgium).

Kendo Grid Persist Row Selection Example
Kendo Grid Persist Row Selection Problem
Now that we have seen the problem, let's see the solution. Why is this happening? When we switched pages the grid no longer knows which row was focused previously. So what we do now is utilize the persistSelection option. Since we have the persistSelection, we no longer need to handle the change event. On dataBound, we find which rows were selected and set the focus to that row.

Here is our solution. You can edit the code below using any text editor but I prefer Visual Studio Code. 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.

Output:

Kendo Grid Persist Row Selection Example
Kendo Grid Persist Row Selection Solution

There you have it. A quick and simple example of how to persist a row selection on a Kendo Grid.

Friday, October 1, 2021

Kendo Chart Using Vue Wrappers Example

This article is an example of how to use the Kendo UI Wrappers for Vue particulary the Kendo Chart. The components in the Kendo UI Wrappers for Vue suite are wrappers of the Kendo UI for jQuery widgets. There is also a Kendo UI Native for Vue suite which has native components designed specially for the Vue framwork.

What prompted me to write this article is a StackOverflow question on how to use it. Perhaps he didn't understand the Kendo documentation so I'm writing this to supplement the Kendo documentation. To better understand this, I'll implement Donut Kendo Chart with 0% Example (which was done in Kendo UI for jQuery) using Kendo UI Wrappers for Vue. We should be able to compare the two implementations and grasp it better.

To begin with, include all the required Kendo styles, libraries, and the specific Kendo Vue package. See the head part of the HTML below. What was loaded? CSS files, jQuery, Kendo, Babel, Vue and the specific Kendo Chart wrapper for Vue.

Let's go to the body. We start off with a div with an id. Inside this div, we place the kendo-chart directive. To configure the chart, we can utilize the Kendo Chart for jQuery API configuration options as props but with a slight naming change. The :theme="'material'" is an example of setting the prop in an API-based options way. This equates to the theme configuration option in Kendo Chart for jQuery. Another option is called the Camel-Case wherein Camel case options in jQuery are converted to Kebab case (e.g. seriesDefaults to series-defaults). Next is the Composite options wherein widget configuration that accept object notation are translated into Kebab case (e.g. title.position to title-position). Now that we are happy with the configuration, it's time to instantiate the Vue app.

We instantiate our Vue app and attach it to the div named donutChart. The data option returns an object with a series field which is then bound to the prop :series="series".

Here is our code. You can edit the code below using any text editor but I prefer Visual Studio Code. 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 Wrappers for Vue or Kendo UI for jQuery for more than just evaluation purposes.

Output:

Kendo Chart Using Vue Wrappers Example
Kendo Chart Using Vue Wrappers

 

There you have it. A simple example of a Kendo Donut Chart implemented with Kendo UI Wrappers for Vue.