Gregory's Blog

Working with JSON and JavaScript


In my series of ColdFusion and Kendo UI articles, I have covered how to use JSON to populate the Kendo UI widgets. However, JSON can be used for much more than that!

JSON objects can be used by nearly every modern language, whether it is C#, Java, or ColdFusion. I personally use JSON nearly every time that I want to transfer data from the server to the client side using JavaScript.

In this article, we will take a quick break from Kendo UI and will introduce you to JSON and AJAX, show you how to create and consume JSON, how to consume JSON on the server using AJAX, and how to work with these JSON objects with JavaScript. Like our other articles, I will also provide the code and real-time examples. 



What is JSON?

Simply put, JSON is a popular string format that is used to exchange data between applications. In web applications, it is typically used to transfer data with AJAX or used when interacting with an API. 

JSON typically contains an array of structures, however, it also can be a single double-quoted string. JSON is not inherently an object but describes object data in JavaScript and other languages. However, once JSON is successfully parsed by JavaScript (or other languages), the JSON information is transformed into a native JavaScript object.


JSON Structure

The example shown below is a typical JSON structure that puts the data elements within an array of structures. However, as we mentioned before, you can also use simple strings in JSON as long as the string is enclosed by double quotes.

The following elements can be used in the JSON string:

  • strings enclosed in double quotes
  • numbers are not quoted
  • objects, typically a structure of key pairs enclosed by braces
  • arrays surrounded by square brackets.
  • boolean values do not use quotes
  • null uses null without quotes

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON for more detailed information.

Here is an example of all of these types. This example is used in Galaxie Blog to populate role dropdowns, and we will be using something quite similar in an upcoming article.

[
   {
      "Description":"All functionality.",
      "RoleId":1,
      "RoleName":"Administrator",
      "Capabilities": ["AssetEditor",
		"EditCategory",
		"EditComment",
		"EditFile",
		"EditPage",
		"EditPost",
		"EditProfile",
		"EditServerSetting",
		"EditSubscriber",
		"EditTemplate",
		"EditTheme",
		"EditUser",
		"ReleasePost"],
      "Notes":null,
      "Active":true
   }
]

JSON Data Handles and Other Extraneous Information

While not technically part of the JSON specification, some HTML5 widget libraries, such as jsGrid, require the JSON to have a data handle. The data handle in the example below is the "data": string.

Some widgets, including Kendo UI, also require the JSON to have a total structure in the JSON to display the total number of records in a grid for pagination purposes.

JSON is a flexible specification, as long as you follow the basic rules, this information can be incorporated. Extra information in the JSON can also be ignored.

JSON Example with a Data Handle

{
   "data":[
      {
         "Email":"myemail@gmail.com",
         "Description":"We have covered how to use JSON to populate Kendo UI objects, but JSON can do much more than that! JSON can be used in nearly every language, however, in this article, we will discuss how to work with JSON objects in JavaScript.",
         "BlogSortDate":"September, 16 2022 23:32:00",
         "MimeType":null,
         "PostAlias":"Working-with-JSON-and-JavaScript",
         "Released":false,
         "Body":"In my series of ColdFusion and Kendo UI articles, I have covered how to use JSON to populate the Kendo UI widgets. However, JSON can be used for much more than that!",
         "Title":"Working with JSON and JavaScript",
         "NumViews":0,
         "Date":"September, 20 2022 00:00:00",
         "FullName":"Gregory Alexander",
      }
   ]
}

JavaScript JSON Related Functions 

The following static JavaScript functions are used with JSON:


Extracting Data from a JSON String 

To extract the value of a single element in the JSON string, use variableName.key. This is identical to getting a value stored in ColdFusion structures. For example, to get the post title in the JSON above, use data.Title.

You can also use bracket notation, just as you would get the value of a ColdFusion HQL column, using variableName["key"]. Using this notation we would use data["Title"]. Both of these statements will do the same thing.


A Brief Introduction to AJAX and JSON 

There are many other ways to elicit a JSON response from the server, but this article will focus on using AJAX on the client side to send an asynchronous HTTP (Ajax) request to a ColdFusion Component or function. The ColdFusion template on the server will process the data and send a JSON response back to the calling AJAX function. We will then inspect the AJAX response and deliver information back to the user using JavaScript.

This AJAX request may be made either by a traditional AJAX statement or performed automatically using the Kendo DataSource when we declare a Kendo DataSource or initialize a Kendo UI widget. 


AJAX Automatically Transforms JSON into JavaScript

It is important to note that if you are using AJAX to consume JSON on the server, the JSON string will automatically be converted into a native JavaScript object when using "json" as the dataType argument. After the JSON string is transformed, you will need to use native JavaScript methods to get at the underlying data in the JavaScript object.


What is the Difference Between AJAX and getJSON?

You may either use jQueries AJAX or getJSON method to fetch JSON data using a get HTTP request. The getJSON function is a simplified version of jQuery's AJAX- however, underneath the hood, the two methods are the same. 

I prefer using the AJAX method as it provides more customization, in particular, I can either enable or disable caching. Technically, there are ways to disable caching using the getJSON function, but they either affect all of the AJAX statements with a global AJAX cache var or they resort to using a timestamp method which I find to be a bit kludgy.

The arguments for the getJSON function are: 

$.getJSON(url, data, success);

Parsing JSON that has been Transformed into a JavaScript Object

There are multiple ways to get the JSON keys and values in a JavaScript object. However,  the approach will differ depending on if you want to extract a single value or multiple records. 

Extracting a Single Value

If you're using a single row of data, or know the index of the row that you want to extract, you can use data[index].key

For example, to get the state name in our result, use data[0].name. JavaScript arrays start at zero, so the first row uses an index of 0. This is identical to the approach used to parse a JSON string, although using JavaScript requires that you specify an index to get the proper row.

Like JSON, you can also use the bracket notation like so:

alert(data[0]['name'])

Here is a full example with the AJAX call:

jQuery.ajax({
	type: 'post', 
	url: '<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc',
	data: { // method and the arguments
		method: "getStates",
		state: "Washington"
	},//..data: {
	dataType: "json",
	success: result, // calls the result function.
	// Simplified error handling
	error: function(ErrorMsg) {
	   console.log('Error' + ErrorMsg);
	}
});//..jQuery.ajax({

function result(data){
	alert(data[0].name);
}


Looping Through Multiple Records

If you have multiple records, you can loop through the JSON and retrieve an item using the following script. I am using bracket notation in this script.

for(var i=0; i < data.length; i++){
	// Get the data held in the row in the array using bracket notation
	alert(data[i]['name'])
}

A complete example with an HTML form populated by AJAX and JSON. A real-time example is provided below.

<script>
	// Get state data from the server
	jQuery.ajax({
		type: 'post', 
		url: '<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc',
		data: { // method and the arguments
			method: "getStates",
			countryId: 233 //United States
		},//..data: {
		dataType: "json",
		success: result, // calls the result function.
		// Simplified error handling
		error: function(ErrorMsg) {
		   console.log('Error' + ErrorMsg);
		}
	});//..jQuery.ajax({

	function result(data){
		for(var i=0; i < data.length; i++){
			// Get the name of the state held in the row in the array and push it to the form
			$("#state" + i).val(data[i]['name']);
		}//for(var i=0; i < result.data.length; i++){..
	}//function result(data){..
</script>

<p>This form is populated by AJAX and JSON</p>
<table width="100%" class="k-content">
<!--- Loop 66 times, some states are territories here --->
<cfloop from="0" to="65" index="i">
  <tr>
	<td align="left" valign="top" class="border" colspan="2"></td>
  </tr>
  <tr>
	<td align="right" style="width: 20%">
		<label for="state<cfoutput>##</cfoutput>">State:</label>
	</td>
	<td>
		<!-- Create the state text input -->
		<input type="text" id="state<cfoutput>#i#</cfoutput>" name="state<cfoutput>#i#</cfoutput>" value="" style="width: 95%">
	</td>
   </tr>
</cfloop>
   <tr>
	 <td align="left" valign="top" class="border" colspan="2"></td>
   </tr>
</table>


Inspecting the Entire Object

Finally, you may inspect the entire object using JavaScript like so:

function result(data){
	// Loop thru the outer object (data)
	for(var i=0; i < data.length; i++){
		// Get the data held in the row in the array. 
		var obj = data[i];
		// Create an inner for loop
		for(var key in obj){
			// Set the values. 
			var attrName = key;
			var attrValue = obj[key];

			alert(attrName);
			alert(attrValue);
		}
	}
}

Complete Example with AJAX:

<script>
	// Get state data from the server
	jQuery.ajax({
		type: 'post', 
		url: '<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc',
		data: { // method and the arguments
			method: "getStates",
			state: "Washington"
		},//..data: {
		dataType: "json",
		success: result, // calls the result function.
		// Simplified error handling
		error: function(ErrorMsg) {
		   console.log('Error' + ErrorMsg);
		}
	});//..jQuery.ajax({

	function result(data){
		// Loop thru the outer object (data)
		for(var i=0; i < data.length; i++){
			// Get the data held in the row in the array. 
			var obj = data[i];
			// Create an inner for loop
			for(var key in obj){
				// Set the values. 
				var attrName = key;
				var attrValue = obj[key];
				// Pop the values up
				alert('Attribute Name: ' + attrName);
				alert('Attribute Value: ' + attrValue);
			}//for(var key in obj){..
		}//for(var i=0; i < data.length; i++){..
	}//function result(data){..
</script>

This page retrieves the state using AJAX and will pop up an alert showing the keys and values found in the object. There should be 8 popups here.


We will use some of what we learned here today in our next article.


Further Reading

This entry was posted on September 22, 2022 at 11:22 PM and has received 85 views.

Cascading Country, State and City Kendo UI Dropdowns


In this article, I will show you how to create sophisticated cascading Kendo Dropdown menus based on the selection of a dependent dropdown, and provide real-time examples.

This article will be more comprehensive than the generic examples provided on the Kendo UI website- I will share how to use multiple input parameters and provide some tips and tricks that I have learned over the years to make your code more reliable and accessible. Unlike the Kendo UI examples on the Telerik site, this article will take you through the whole process including building the remote end-point. 

In this example, we will create and demonstrate a Country, State, and City cascading dropdown and demonstrate the ease of use when using the search widget to quickly select a value from a long list of items. These comprehensive dropdowns will contain all of the countries, states, and major cities in the world.

We will also be going over various Kendo events and will change the UI based on how many records are being returned in the JSON object.

I will also provide the database files and all of the code on GitHub for others to use. The Kendo UI-related code will be compatible with the open-source version of Kendo in order to use this in your own projects and potentially distribute your code.

Like the rest of this series of Kendo articles, we will be using ColdFusion as our service end-point, however, if you use another server-side language, you should be able to follow along.



Cascading Country State and City Cascading Dropdowns Example Application

We are going to demonstrate the Kendo Dropdowns with a country, state, and city cascading dropdown. The first dropdown will display all of the countries of the world, the second dropdown will only display the states relevant to the selected country, and the third dropdown will show the cities within the chosen state. When there are no states for a given country, we will disable the state dropdown and query the database for all cities for the selected country instead of the state using an extra parameter that we will send to the server.

Click on the button below to see the live example.


Our Cascading Kendo Dropdowns Methodology

Telerik suggests using the cascadeFrom and other cascade-specific arguments when creating cascading dropdowns, however, we are going to use a different approach.

Telerik's cascading implementation works fine, however, I have personally had difficulties with it. It is more complex and problematic than just saving the required elements needed for the dependent dropdowns in a hidden field. I have also had issues with the variables not being defined when I need them as the widgets are typically found in a document-ready scope and the variables are unavailable outside of this scope. Saving the selected values in a hidden form field allows us to use this selection for other logic outside of the document-ready scope. It is also more difficult to pass in multiple arguments to the Kendo data source that we are doing here, and you can't use multiple Kendo cascadeFrom arguments. Using this approach allows you to send the selections of multiple dropdowns to the controller instead of just using one. I also don't like that all of the widgets need to be tightly bound together in a single scope and prefer an approach that allows for better separation of logic.

Gregory's approach:

  1. Use the drop-down widgets onChange method to:
    1. Capture and store the user's selection into a hidden form field.
    2. Refresh any dependent dropdowns using the widgets refresh method.
  2. The data sources belonging to the dependent dropdowns will pass the necessary values in the hidden form fields to the remote end-point.
  3. If necessary, use the DataSource's onChange method to validate the data and perform any additional logic.

I would like to add an additional rule to keep the Kendo dropdown and data source separate for better separation of logic and code reuse, but this is not strictly necessary.

Using this purely state-driven approach to connecting the menus is reliable and I have used this approach in production environments for the last ten years.


Introducing Kendo UI Events

Every widget has a number of events. There are traditional events that are similar to typical DOM events, such as the onChange event, and additional events that occur when the dropdowns acquire data such as the dataBound event. Additionally, Kendo's abstractions, such as the Kendo dataSource, also have their own events. To see a list of the events for a particular widget, go to the widget example on the Telerik site, and click on the API button at the bottom of the page. 

In this article, we will use the change event for the dropDownList to save the values that the user selected into hidden form fields and to trigger the dependent dropdowns. We are also going to use the Kendo dataSource to make changes in the UI.


Creating the Countries - States - Cities Database

We will be using the open-source Countries- States and Cities GitHub repository created by Darshan Gada. This repository is updated several times a year and contains all of the necessary files to create and maintain the database.

If you want to follow along and create your own MySql World database, go to the repository and locate the country SQL file in the SQL folder. We need to open and download the world.sql file. The URL to this file as of 2022 is https://github.com/dr5hn/countries-states-cities-database/blob/master/sql/world.sql

Do not download any other file- we only need the world.sql file to create the database.

Once the world.sql file is downloaded, copy and paste the code into your MySQL query, or import the SQL file into your MySql database. This file can also be used to update the database. If you need assistance please refer to your MySql documentation.

Once you're done, create a ColdFusion data source.


Creating the back-end ColdFusion Service Endpoint

We are using a ColdFusion component, WorldCountries.cfc, to serve as the service endpoint for the Kendo Datasource that will populate the Kendo dropdowns. This component will prepare and send the data from the world database back to each cascading dropdown as JSON.

The getCountries WoldCountries.cfc method

This method populates the country dropdown list in the interface and should be self-explanatory. Since the dropdowns can be quite long (there are thousands of potential cities for example), we only want to send back what is necessary here, namely the country id and name.

This method does not take any arguments but simply queries our World MySql database and converts the ColdFusion query object using our CfJson object as a JSON object.
See https://www.gregoryalexander.com/blog/2022/7/13/Using-ColdFusion-to-Populate-Kendo-UI-Widgets for more information on how we are converting the ColdFusion query object to JSON.

<cffunction name="getCountries" access="remote" returnformat="json" output="true"
		hint="Gets the world countries">

	<cfquery name="Data" datasource="cityDb">
		SELECT id, name FROM countries
		ORDER BY name
	</cfquery>

	<!--- Convert the query object into JSON using the convertCfQuery2JsonStruct method --->
	<cfinvoke component="#application.cfJsonComponentPath#" method="convertCfQuery2JsonStruct" returnvariable="jsonString">
		<cfinvokeargument name="queryObj" value="#Data#">
		<cfinvokeargument name="includeTotal" value="true">	
	</cfinvoke>

	<!--- Return it. --->
	<cfreturn jsonString>

</cffunction>

The getStates method

This function is similar to the getCountries method above, however, it takes the country id as an argument and filters the results based on the selected country. It will only retrieve states that belong to the country that was selected by the user.

<cffunction name="getStates" access="remote" returnformat="json" output="true"
		hint="Gets the world states by a variety of optional arguments">
	<cfargument name="countryId" type="string" required="false" default="" />

	<cfquery name="Data" datasource="cityDb">
		SELECT id, name FROM states
		WHERE 0=0
	<cfif len(arguments.countryId)>
		AND country_id = <cfqueryparam value="#arguments.countryId#" cfsqltype="integer">
	</cfif>
		ORDER BY name
	</cfquery>

	<!--- Convert the query object into JSON using the convertCfQuery2JsonStruct method --->
	<cfinvoke component="#application.cfJsonComponentPath#" method="convertCfQuery2JsonStruct" returnvariable="jsonString">
		<cfinvokeargument name="queryObj" value="#Data#">
		<cfinvokeargument name="includeTotal" value="true">	
	</cfinvoke>

	<!--- Return it. --->
	<cfreturn jsonString>

</cffunction>

The getCities Method

This method is a little different than the getStates method as it accepts two optional arguments- the country and state id. A country might have cities, but not states. If this is the case, we need to return the cities that belong to a country, yet don't reside in their own state. We will cover this scenario further later in this article. 

<cffunction name="getCities" access="remote" returnformat="json" output="true"
		hint="Gets the world cities by a variety of optional arguments">
	<cfargument name="countryId" type="string" required="false" default="" />
	<cfargument name="stateId" type="string" required="false" default="" />

	<cfquery name="Data" datasource="cityDb">
		SELECT id, name FROM cities
		WHERE 0=0
	<cfif len(arguments.countryId)>
		AND country_id = <cfqueryparam value="#arguments.countryId#" cfsqltype="integer">
	</cfif>
	<cfif len(arguments.stateId)>
		AND state_id = <cfqueryparam value="#arguments.stateId#" cfsqltype="integer">
	</cfif>
		ORDER BY name
	</cfquery>

	<!--- Convert the query object into JSON using the convertCfQuery2JsonStruct method --->
	<cfinvoke component="#application.cfJsonComponentPath#" method="convertCfQuery2JsonStruct" returnvariable="jsonString">
		<cfinvokeargument name="queryObj" value="#Data#">
		<cfinvokeargument name="includeTotal" value="true">			
	</cfinvoke>

	<!--- Return it. --->
	<cfreturn jsonString>

</cffunction>

The First Country Dropdown

The first dropdown, the country drop-down list is the easiest to understand.

This dropdown will have a detached Kendo data source (you may use inline if you want) that grabs all of the countries from the ColdFusion remote endpoint. There is nothing inherently different than the previous Introducing the various forms of Kendo Dropdowns in the data source or widget initialization.

Where this differs is that after we initialize the widget we will use an onChange event on the DropDown list. We will explain further below.


Create The Country Dropdown Kendo Datasource

This data source simply invokes the WorldCountries.cfc getCountries method which returns a JSON object with the countryId and country of all of the countries in the world. There is nothing out of the norm here that we did not already cover in previous articles.

// ---------------------------- Top level country dropdown. ----------------------------
			
// ---------- Country Datasource. ----------
var countryDs = new kendo.data.DataSource({
	transport: {
		read: {
			cache: false,
			// Note: since this template is in a different directory, we can't specify the cfc template without the full path name.
			url: "<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc?method=getCountries", // The cfc component which processes the query and returns a json string.
			dataType: "json",
			contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
			type: "POST"
		}
	},
});//var countryDs...

Initialize the Country DropDownList

Here we are simply extracting data from the countryDs Kendo data source, using the countryId as the value of the dropdown, and using the country name as the label.

There is nothing unusual that has not been covered in our previous article other than having an onChange event that calls the onCountryChange function which will be discussed below. 

// ----------- Country Dropdown. -----------
var countryDropdown = $("#countryDropdown").kendoDropDownList({
	optionLabel: "Select Country...",
	dataValueField: "id",
	dataTextField: "name",
	// Create the onChange method that will create the next dropdown list
	change: onCountryChange,
	filter: "contains",
	dataSource: countryDs
}).data("kendoDropDownList");//var countryDropdown...

The Country DropDownList onChange Event

The change event is fired off when the user selects a new country. Here, we are going to simply save the selected countryId into a hidden form field. 

// ------------ onChange Event ------------
// On change function that will enable the next dropdown menu if a value is selected.
function onCountryChange(e){
	// Get the selected value
	var id = this.value();
	// Save the selected value in a hidden form
	$("#selectedCountry").val(this.value());
	// If the user selected a value, enable and refresh the next dropdown list
	if (id != '') {
		// Enable the state dropdown
		stateDropdown.enable();
		// Refresh the state dropdown
		stateDropdown.dataSource.read();
	}//..if (id != '') {

	// Disable the city button
	// Get the Kendo button at the end of the interface
	var cityButton = $("#cityButton").data("kendoButton");
	// Disable the the button
	cityButton.enable(true);

}//function onCountryChange(e)...

The Second State Dropdown

The second dropdown, the State DropDownList, will show all of the states for the selected country. It differs slightly from the first country dropdown as it also has an onChange event on the data source as well as an onChange event for the dropdown widget. 

The onChange event on this widgets data source validates that a state is returned for the selected country. If the state exists, this will fire off an event to enable and populate the cities dropdown that belong to the selected state. However, if a state does not exist, it will deactivate the state dropdown and populate the cities dropdown with the cities that belong to the country as some countries have cities, but not states (i.e. Vatican City). We will cover this dataSource event below in more detail.


The States Kendo Datasource

Other than the onChange method, this data source is similiar to the other data sources that we have already covered. We are simply passing the value of the selected countryId that was stored into the hidden form to the back end ColdFusion remote endpoint and calling the getStates method in the WorldCountries ColdFusion component. 

The onChange method requires more elaboration...

Here, we are validating the data to see if the country does not exist and changing the UI when the state does not exist for the selected country. When the state does not exist, we are enabling the cities menu calling the getCities method, and passing the countryId. This will extract any cities that may exist for the selected country, but not the state. Remember that a country may exist without states. 

If there are states for the selected country, we disable the city dropdown until a state has been selected. We are also disabling the city button (not the dropdown though) at the bottom of the page.

This optional validation step is indicated in step 3 of our methodology.

// -------------------------------- 2nd State Dropdown. --------------------------------

// ----------- State Datasource. -----------

// state populated by the first country dropdown.
var stateDs = new kendo.data.DataSource({
	transport: {
		read: {
			cache: false,
			// The function allows the url to become dynamic to append additional arguements.
			url: function() { return "<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc?method=getStates&countryId=" + $('#selectedCountry').val(); },
			dataType: "json",
			contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
			type: "POST"
		}
	},
	// Function to enable the city dropdown when a state does not exist for a selected country. This will only fire when there is no data in the Kendo datasource and will enable the city dropdown in order for the user to still be able to pick a city. This is fired everytime there is a change to the state datasource
	change: function() {
		// Get the datasource length
		var data = this.data();
		// Set a var for the city dropdown list.
		var cityDropdown = $("#cityDropdown").data("kendoDropDownList");
		// If there are no states for this country...
		if (!data.length){
			// Disable the state dropdown
			stateDropdown.enable(false);
			// Now enable the city dropdown list.
			cityDropdown.enable();
			// Refresh the city dropdown
			cityDropdown.dataSource.read();
		} else {
			// Disable the city dropdown
			cityDropdown.enable(false);
		}//if (! data.length){..

		// Get the Kendo button at the end of the interface
		var cityButton = $("#cityButton").data("kendoButton");
		// Disable the the button
		cityButton.enable(true);

	}//change: function() {..
});//var stateDs...

Initializing the second state dropdown

Here, we are using the Id as the value of the dropdown and the state name as the label. We are also setting the autobind argument to false as we want to control when to fire off this dropdown based upon an event. This is necessary for all of the dependent dropdowns. Our change event is going to call another function that will save the selected value into the hidden form and we will cover that next. Finally, we are setting the state menu to select the first item in the list (select(0)) which will set the dropdown to display our 'Select State...' hint.

// Create the state dropdown list
var stateDropdown = $("#stateDropdown").kendoDropDownList({
	optionLabel: "Select State...",
	dataValueField: "id",
	dataTextField: "name",
	autoBind: false,
	enable: false,
	dataSource: stateDs,
	filter: "contains",
	change: onStateChange,
}).data("kendoDropDownList");//var stateDropdown...

// After the state is initialized, set the value to be the option label.
stateDropdown.select(0);

The States Widget onChange Event

Like all of the dropdowns here, when the user selects a state, we are simply enabling and firing off the next city dropdown menu and saving the selected stateId into a hidden form.

// ------------ onChange Event ------------
// Function to enable the last city dropdown menu
function onStateChange(e){
	// Get the next dropdown list.
	var cityDropdown = $("#cityDropdown").data("kendoDropDownList");
	// Save the id in a hiden form in order to get at it in the next dropdown
	var id = this.value();
	// If the user selected a value, enable and refresh the next dropdown list
	if (id != '') {
		$("#selectedState").val(this.value());
		// Enable the city dropdown list.
		cityDropdown.enable();
		// Refresh the city dropdown
		cityDropdown.dataSource.read();
	}//..if (id != '') 

}//function onStateChange(e)

The Last City Dropdown

The last city dropdown sends multiple arguments to the ColdFusion end-point. This sends both the selected country and the state to the getCities method in the WorldCountries.cfc ColdFusion component. This allows us to extract the cities by country or state. We won't have any duplicates here as both the state and the city will belong to the same country. Finally, we will set the state of the button at the bottom of the interface and enable the button if a city was found.

The City Kendo DataSource

// -------------------------------- Last City Dropdown. --------------------------------

// ----------- City Datasource. -----------

// City populated by the second state dropdown.
var cityDs = new kendo.data.DataSource({
	transport: {
		read: {
			cache: false,
			// The function allows the url to become dynamic to append additional arguements. Here we are sending both the countryId and the stateId. There are some countries that do not have states.
			url: function() { return "<cfoutput>#application.baseUrl#</cfoutput>/demo/WorldCountries.cfc?method=getCities&countryId=" + $('#selectedCountry').val() + "&stateId=" + $('#selectedState').val(); },
			dataType: "json",
			contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
			type: "POST"
		}
	},
});//var cityDs...

The City Dropdown Initialization

The initialization approach is identical to the other widgets. Simply put, we are using the cityId as the value of the dropdown, the city name as the label, disabling autobind, and setting the default dropdown option to the optionLabel, 'Select City...'.

// ----------- City Dropdown. -----------
var cityDropdown = $("#cityDropdown").kendoDropDownList({
	optionLabel: "Select City...",
	dataTextField: "name",
	dataValueField: "id",
	autoBind: false,
	enable: false,
	filter: "contains",
	change: onCityChange,
	dataSource: cityDs
});//var cityDropdown...

// After the city is initialized, set the value to be the option label.
cityDropdown.select(0);

The Cities Dropdown onChange Event

Here, when a city is selected, we will save the selection into a hidden form and activate the city button at the bottom of the interface, otherwise, we will disable the button.

// ------------ onChange Event ------------
// Function to enable the button to launch a new window showing the details
function onCityChange(e){
	var id = this.value();
	// If the user selected a value, enable teh button at the end of the interface
	if (id != '') {
		// save the selected value in a hidden form
		$("#selectedCity").val(this.value());
		// Get the Kendo button at the end of the interface
		var cityButton = $("#cityButton").data("kendoButton");
		// Enable the button
		cityButton.enable(true);
	} else {
		// Disable the button at the bottom of the UI
		cityButton.enable(false);
	}//..if (id != '') {
}//function onStateChange(e)

// Create the last button.
var cityButton = $("#cityButton").kendoButton({
	enable: false
}).data("kendoButton");

Client Side HTML

We will wrap this article up with the client-side HTML. Here we have our hidden form elements that store the user's choices and the select elements that will be used for the Kendo DropDowns and disabling the children dropdowns. 

<!-- Hidden inputs to hold selected values. -->
<input type="hidden" id="selectedCountry" name="selectedCountry" />
<input type="hidden" id="selectedState" name="selectedState" />
<input type="hidden" id="selectedCity" name="selectedCity" />

<p>Select a country, state and city. Not all countries have states or cities, take Anguilla for example.<br/>
Note: in this example, the button at the end of the interface is for display purposes only.</p>
<table width="100%" class="k-content">
  <tr>
	<td align="left" valign="top" class="border" colspan="2"></td>
  </tr>
  <tr>
	<td align="right" style="width: 20%">
		<label for="countryDropdown">Country:</label>
	</td>
	<td>
		<!-- Create the country dropdown -->
		<select id="countryDropdown" name="countryDropdown" style="width: 95%"></select>
	</td>
   </tr>
   <tr>
	 <td align="left" valign="top" class="border" colspan="2"></td>
   </tr>
   <tr>
	<td align="right" style="width: 20%">
		<label for="stateDropdown">State:</label>
	</td>
	<td>
		<!-- Create the state dropdown -->
		<select id="stateDropdown" name="stateDropdown" style="width: 95%" disabled></select>
	</td>
  </tr>
  <tr>
	<td align="left" valign="top" class="border" colspan="2"></td>
  </tr>
  <tr>
	<td align="right" style="width: 20%">
		<label for="cityDropdown">City:</label>
	</td>
	<td>
		<!-- Create the state dropdown -->
		<select id="cityDropdown" name="cityDropdown" style="width: 95%" disabled></select>
	</td>
  </tr>
  <tr>
	<td align="left" valign="top" class="border" colspan="2"></td>
  </tr>
  <tr>
	<td></td>
	<td>
		<button id="cityButton" name="cityButton" class="k-button k-primary" type="button" disabled>View City Details</button>
	</td>
  </tr>
</table>

 

This entry was posted on September 16, 2022 at 10:08 PM and has received 151 views.

Introducing the various forms of Kendo Dropdowns


There are three major types of Kendo Dropdowns, the Kendo DropDown List, ComboBox, and the Multi-Select. All of these Kendo widgets are used to enhance the traditional HTML select input and add rich HTML5 functionality.

However, unlike a traditional HTML dropdown, it offers quite a few features such as having a search input at the top of the menu to allow users to quickly find their intended choice. This can save quite a bit of time and agony for the end-users when the dropdown contains a list of choices as the user can quickly type in a value in the search field and select the intended choice. I have often wished that other major websites have this capability when I am asked to select a value from a long list of choices, such as selecting my country and state for example.

Additionally, these drop-downs support sophisticated data operations, such as virtualization and grouping, and these elegant dropdowns take on the characteristics of your chosen theme and you can apply Kendo Templates to the dropdowns to extend the functionality such as adding images to the dropdown choices.

These controls can be data bound to a traditional select input using the option tags, bound to a local JavaScript array, or bound to a remote data service using a server-side language such as ColdFusion. Refer to our previous Using ColdFusion to Populate Kendo UI Widgets for in-depth examples of each of these options. If you want an example of how to bind the widgets to a local JavaScript array, see Using ColdFusion to Synchronize Time Across Time Zones.

In this article, we are going to discuss the differences between these elements and show some examples of each dropdown type that binds the Kendo DataSource to a server-side end-point using ColdFusion.



The Kendo DropDownList

In its most basic form, the Kendo DropDown List replicates the normal HTML dropdown in that the user can only choose among predetermined values.

This dropdown can either be created manually by creating your option tags in HTML or it can be bound to data using JavaScript arrays or populated by a remote service.


The Kendo ComboBox 

The Kendo ComboBox is identical to the Kendo DropDownList with just one main difference- the ComboBox also allows the user to enter a custom value in the search bar. If the value does not match one of the values, the ComboBox allows you to launch a new interface to allow the user to enter the new value.

To put it another way- often a ComboBox is termed as an editable dropdown list whereas the DropDownList is a non-editable dropdown list. For more information on the ComboBox across various platforms see https://en.wikipedia.org/wiki/Combo_box.

To illustrate, in the example below, I developed an interface to allow the administrators to specify fonts for unique portions of the page, however, if the user types in a font that does not exist in the search bar at the top of the dropdown widget, the user is also able to upload a new font and have it displayed in the Kendo ComboBox.


Kendo Multi-Select

Unlike the dropdown list and ComboBox widgets, the Kendo Multi-Select is a unique dropdown widget that allows the user to select multiple values from a dropdown list. This sophisticated widget can be used to replace a long list of checkboxes on a form allowing the user interface to be streamlined. 

Like the DropDownList and ComboBox, this widget also has search capabilities allowing the user to quickly select multiple values. I use this widget on the blog to allow the user to select certain categories when performing a site search. The Kendo MultiSelect component greatly simplifies the Galaxie Blog search interface- without this component, I would have to loop through every category and build checkboxes making the interface very long and confusing.


Similarities and Differences Between The Kendo Dropdowns

As we just mentioned, if configured properly with the filter argument, the Kendo DropdownList and ComboBox offer nearly identical functionality. Nearly all of the code is interchangeable between the two widgets. 

In fact, in the ComboBox example below, you can change this widget to a dropdown list by changing the first line of the code below by substituting the string 'kendoComboBox' with 'kendoDropDownList'. However, if you change the widget to a Kendo Dropdown list it will not add a new item to the list when you enter a value using the search function that is not found.

Remember that the DropDownList may also be considered a non-editable dropdown whereas the ComboBox is an editable dropdown. In Kendo UI's implementation, this simply means that if the search term is not found you can add a new item to the list (and optionally to the database) with a new interface.

You may also replicate the functionality of the ComboBox with the DropDownList, but it will require extra code that causes the dropdown to refresh if a new item has been added using the noDataTemplate which we will cover later on. I would not be surprised to see Telerik consolidate the two widgets eventually. 

The purpose of the Kendo MultiSelect widget is a different matter- it is meant to allow the user to select multiple values using a familiar dropdown interface. As we have already covered, this widget can also replace a long list of checkboxes making it easier for the user to select an option and it clears up valuable real estate on the screen.

The code used to initialize all of these widgets is nearly identical. You can usually substitute one for another by changing a couple of lines of code. There are some other minor differences however, placeHolder is used for the ComboBox and MultiSelect, and optionLabel is used for the dropdown list to make an inline hint when nothing is selected for example.

We will cover the widget's basic initialization below.


ComboBox Javascript Initialization

The only difference between this and the code below is the string kendoComboBox on the first and last lines, and placeHolder in the second line. The rest of the code is identical between the two widgets.

var blogNameFontDropdown = $("#blogNameFontDropdown").kendoComboBox({
	placeHolder: "Select...",
	autoBind: false,
	dataTextField: "Font",
	dataValueField: "FontId",
	template: '<label style="font-family:#:data.FontFace#">#:data.Font#</label>',
	// Template to add a new type when no data was found.
	noDataTemplate: $("#addFont").html(),
	filter: "contains",
	dataSource: fontDs,
}).data("kendoDropDownList");

See https://demos.telerik.com/kendo-ui/combobox/index for more information.


DropDownList Javascript Initialization

The only difference between this and the code below is the string kendoDropDownList on the first and last line and optionLabel in the second line.

var blogNameFontDropdown = $("#blogNameFontDropdown").kendoDropDownList({
	optionLabel: "Select...",
	autoBind: false,
	dataTextField: "Font",
	dataValueField: "FontId",
	template: '<label style="font-family:#:data.FontFace#">#:data.Font#</label>',
	// Template to add a new type when no data was found.
	noDataTemplate: $("#addFont").html(),
	filter: "contains",
	dataSource: fontDs,
}).data("kendoDropDownList");

See https://demos.telerik.com/kendo-ui/dropdownlist/index for more information.


Kendo MultiSelect Javascript Initialization

The line to initialize the multi-select is kendoMultiSelect, and like the Kendo ComboBox, this uses the placeholder argument to specify a hint when nothing is selected.

var blogNameFontDropdown = $("#blogNameFontDropdown").kendoMultiSelect({
	placeholder: "Select...",
	autoBind: false,
	dataTextField: "Font",
	dataValueField: "FontId",
	template: '<label style="font-family:#:data.FontFace#">#:data.Font#</label>',
	// Template to add a new type when no data was found.
	noDataTemplate: $("#addFont").html(),
	filter: "contains",
	dataSource: fontDs,
}).data("kendoMultiSelect");

See https://demos.telerik.com/kendo-ui/multiselect/index for more information.


The Kendo DataSource Used for these Widgets

This particular data source acquires the data remotely using a get method from the ProxyController.cfc ColdFusion component and returns the data as a JSON object. It should be noted that all of the widget examples are using the same exact fontDs Kendo data source with no code changes. 

As we have stated in other articles, you can use the Kendo Datasource inline within the various widgets, or create a unique data source that is detached from the widget. I typically detach the Kendo DataSource in order to reuse it as we have done here. The data source can be reused for nearly all of the kendo widgets, even the grid. In the future, I plan on writing new articles on the Kendo Grid and will reuse the very same Kendo Datasource that I have used here.

Here is the fontDs Kendo DataSource Javascript initialization that is used for all of these examples:

var fontDs = new kendo.data.DataSource({
	transport: {
		read: {
			cache: false,
			// Note: since this template is in a different directory, we can't specify the cfc template without the full path name.
			url: function() { // The cfc component which processes the query and returns a json string. 
				return "<cfoutput>#application.baseUrl#</cfoutput>/common/cfc/ProxyController.cfc?method=getFontsForDropdown"; 
			}, 
			dataType: "json",
			contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
			type: "GET" //Note: for large payloads coming from the server, use the get method. The post method may fail as it is less efficient.
		}
	} //...transport:
});//...var fontDs...

See https://docs.telerik.com/kendo-ui/api/javascript/data/datasource for more information.


The ColdFusion Remote Endpoint for the DataSource

The Kendo Datasource used for all of these widgets use the same ColdFusion remote endpoint service that is provided by the ProxyController.cfc component. 

If you're trying to follow along with ColdFusion, please see https://www.gregoryalexander.com/blog/2022/7/13/Using-ColdFusion-to-Populate-Kendo-UI-Widgets and download the CFJson ColdFusion component if you don't already have it. This component is necessary to convert ColdFusion objects into JSON used by the Kendo Datasource. 

Most of this code should be self-explanatory, we are simply querying the database using ColdFusion ORM and returning a JSON object without a data handle. We can also use native ColdFusion queries using the 'convertCfQuery2JsonStruct' method in the same cfc.

When using a remote service to populate Kendo controls you must use 'access="remote"' on the ColdFusion method. See https://www.gregoryalexander.com/blog/2022/7/13/Using-ColdFusion-to-Populate-Kendo-UI-Widgets for more information.

<cffunction name="getFontsForDropdown" access="remote" returnformat="json" output="false" 
		hint="Returns a json array to populate the font dropdown.">
	<!--- Note: the csrfToken is not required for this query is also used on the external blog (non-admin). This query is not secured. --->

	<!--- Get the fonts, don't use the cached values. --->
	<cfquery name="Data" dbtype="hql">
		SELECT DISTINCT new Map (
			FontId as FontId, 
			Font.Font as Font
		)
		FROM 
			Font as Font
	</cfquery>

	<!--- Return the data as a json object. --->
	<cfinvoke component="#jsonArray#" method="convertHqlQuery2JsonStruct" returnvariable="jsonString">
		<cfinvokeargument name="hqlQueryObj" value="#Data#">
		<cfinvokeargument name="includeTotal" value="false">
		<cfinvokeargument name="includeDataHandle" value="false">
	</cfinvoke>

	<!--- Return the json string. --->
	<cfreturn jsonString>

</cffunction>

Using Kendo UI Templates in the Dropdowns

Kendo UI Templates can be used to customize the display or to extend the basic dropdown functionality. We are going to briefly go over some of the most basic information in order for you to better understand how we are using templates in this article.

Kendo UI uses its own proprietary syntax that is similar to ColdFusion in that it uses hashes to render JavaScript variables.


Kendo UI Template Syntax

In a nutshell, the following syntax is used to render JavaScript variables:

  • #= javascriptVar # Renders the value as HTML
  • #:  javascriptVar # Uses HTML to encode JavaScript values
  • # if (true) { # ... non-script content here ... # } # Conditional inline statement.

Common Dropdown Template Types

These most commonly used templates are available to the ComboBox, DropDownList, and MultiSelect:

  • FooterTemplate
  • Header Template
  • No DataTemplate
  • and more...

For more information regarding the available templates, look at the widgets API on the Kendo site.


Inline versus External Templates

A Kendo Template can either be inline or an external template. An inline template is typically one line of code or it can be a simple string that is used for display purposes. For example, an inline template can be as simple as this:

  noDataTemplate: '<p>Font does not exist!</p>'

An external template is a JavaScript snippet wrapped with an Id that is used to specify the template in the widget's initialization script along with the type 'tex/x-kendo-template':

<script id="javascriptTemplate" type="text/x-kendo-template">
     <p>Font does not exist</p>
</script>

An external template can also be stored remotely on a different file share or server, but we won't cover this here.

We will show you examples of both templates in the code below, but in a nutshell, you will want to use an inline template for simple display purposes and use an external template for more complex interfaces.

See https://docs.telerik.com/kendo-ui/framework/templates/overview for more information. 


Embedding Font Faces Inside the Dropdown Options Using an Inline Kendo Template

To show off the capabilities of the Kendo Templates, we are going to use the template to dynamically display the font face within all of these Kendo dropdowns. Every new option in the dropdown will display the font face. This can be done with normal HTML dropdowns but it requires a lot of manual HTML and CSS code to fix the font to the new options in the dropdown. 

With a Kendo template, we can specify all of the logic in a single line using a Kendo inline template like so:

template: '<label style="font-family:#:data.FontFace#">#:data.Font#</label>',

This particular template is used in all of the Kendo dropdown examples that we have covered so far to display the proper font inside all of the dropdown values. However, the font display was disabled for iOS clients as more logic is necessary to load the fonts, and we don't have time to add the code at this time.

We will go over the details of this code later in the article.


Using the Kendo NoData Template

The Kendo No Data Template is used when there is no data or when the user types in a value that is not found in the dropdown. It can be used to display feedback to the user, or used to serve as a bridge to a different interface.

I use the No Data Template frequently in Galaxie Blog to allow the users to add a new option to the dropdowns when the search term that they use does not match any of the existing options. For example, when editing or creating a post, the user has a multi-select dropdown to assign the post to certain categories. If the category does not exist, the no data template is used to allow the user to add a new category without having to exit the post interface.  The no-data template acts as a bridge to open up a new category interface saving time for the user. 

In the example below, we will be using an external template instead of the inline template that we used above. The template will be displayed whenever there is no data, or when the search term was not found. This no-data template can be used for all of the dropdown widgets that are covered here and there will be no changes to the code. 

<!--- External template to add a new font. This template can be used for the ComboBox, DropDownList and MultiSelect.  ---> 
<script id="addFont" type="text/x-kendo-tmpl">
	<div>
		Font not found. Do you want to add '#: instance.filterInput.val() #'?
	</div>
	<br />
	<button class="k-button"Add Font</button>
</script>

In this example, the script Id, in this case, 'addFont' will be used to identify the template when we initialize the widget- the type 'text/x-kendo-tmpl' indicates that this is a Kendo template. The string 'instance.filterInput.val()' will display the search term that was used, and we are placing an HTML button to launch a new font interface at the bottom of the template. The rest of the code should be self-explanatory. 

If you want to see the noData template in action, open up the second button below and type in 'foo' in the search control at the top of the widget. You should see an interface that provides feedback that 'foo' was not found and the button will be displayed at the end of the interface. In this example, the button is for display purposes only and is disabled. The other two controls will only show that no data is available.

See https://docs.telerik.com/kendo-ui/api/javascript/ui/dropdownlist/configuration/nodatatemplate for more information.


See these Widgets in Action

In the next article, I will cover more advanced functionality of these dropdowns and show you how to use cascading dropdowns for each of these widgets.

 

This entry was posted on September 7, 2022 at 12:30 PM and has received 128 views.

Using ColdFusion to Populate Kendo UI Widgets


There are multiple ways to populate Kendo widgets with data. Take a simple dropdown, you can populate a dropdown the same way that you would build a simple HTML dropdown using a ColdFusion query loop or create a static dropdown by building the HTML option tags in the dropdown manually. You can also use Javascript arrays as the data source for a dropdown, or other Kendo HTML5 widgets, but to leverage the full dynamic potential of the Kendo widgets, you need to use a server-side language, such as ColdFusion, to query a database and return the data as JSON. 


Table of Contents


Populating a Kendo widget with static HTML

Here is an example of a simple Kendo dropdown list using static HTML:

<script>
	// ---------------------------- Kendo datasource for the dropdown. ----------------------------
	var bestLanguageDs = new kendo.data.DataSource({
		transport: {
			read: {
				cache: false,
				// Note: since this template is in a different directory, we can't specify the cfc template without the full path name.
				url: function() { // The cfc component which processes the query and returns a json string. 
					return "<cfoutput>#application.baseUrl#</cfoutput>/demo/Demo.cfc?method=getBestLanguage"; 
				}, 
				dataType: "json",
				contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
				type: "GET" //Note: for large payloads coming from the server, use the get method. The post method may fail as it is less efficient.
			}
		} //...transport:
	});//...var bestLanguageDs...

	// ---------------------------- Kendo dropdown. ----------------------------
	var serverSideLanguageDropdown = $("#serverSideLanguageDropdown").kendoDropDownList({
		optionLabel: "Select...",
		autoBind: false,
		dataTextField: "label",
		dataValueField: "value",
		filter: "contains",
		dataSource: bestLanguageDs,
	}).data("kendoDropDownList");
</script>


 

Populating the Kendo DataSource with a local Javascript array

For most widgets, you can also bind the Kendo dataSource of the widget to a local Javascript array. This is useful if you don't have the data in the database. Note the arrServerSideLanguage Javascript array is bound to the DataSource variable in the Kendo dropdown. We will cover the Kendo DataSource more extensively in the section below.

<script>
	// Create a Javascript array to populate the Kendo dropdown
	var arrServerSideLanguage = [
		{"label":"Adobe ColdFusion","value":"ACF"},
		{"label":"Lucee","value":"Lucee"}
	]	

	// Create the Kendo dropdown
	var serverSideLanguage = $("#serverSideLanguage").kendoDropDownList({
		optionLabel: "Select...",// Default label
		dataTextField: "label",// Dropdown label
		dataValueField: "value",// Dropdown value
		filter: "contains",// Search filter on the dropdown
		dataSource: arrServerSideLanguage,// The datasource takes the Javascript array to populate the control
	}).data("kendoDropDownList");
</script>


Binding the Kendo control to a ColdFusion remote data service

Most often you will be binding a Kendo widget to a remote endpoint. This will be a multi-step process. 

Don't worry if you don't completely understand this tutorial, this article is meant as an introduction to the process and we will cover these steps again.

  1. First, we need to create a Kendo DataSource that will handle our Ajax operations and call a ColdFusion server-side template. In this example, our service endpoint is Demo.cfc.
  2. Our endpoint will be a component on a ColdFusion server that retrieves data from a database and packages the data into a JSON object. In order to do this, we need to download the CfJson component for ColdFusion.
  3. Once the data is prepared on the server, we will return it to the client using Ajax and pass the JSON data object to the Kendo widgets DataSource.

Create a Kendo DataSource and the dropdown on the client to invoke the ColdFusion service

The Kendo DataSource is a component that allows you to use local Javascript arrays or remote XML, JSON, or JSONP data to populate the various Kendo controls. The DataSource allows for server-side sorting, paging filtering, grouping, and data aggregates. 

The Kendo DataSource handles the necessary AJAX operations and makes an AJAX post to the server found in the URL argument below.

You will see other Kendo examples where all of the Kendo DataSource logic is embedded inside of the widget. I typically separate the Kendo DataSource from the widget as it allows me to potentially reuse the data for other controls. 

<script>
	// ---------------------------- Kendo datasource for the dropdown. ----------------------------
	var bestLanguageDs = new kendo.data.DataSource({
		transport: {
			read: {
				cache: false,
				// Note: since this template is in a different directory, we can't specify the cfc template without the full path name.
				url: function() { // The cfc component which processes the query and returns a json string. 
					return "<cfoutput>#application.baseUrl#</cfoutput>/demo/Demo.cfc?method=getBestLanguage"; 
				}, 
				dataType: "json",
				contentType: "application/json; charset=utf-8", // Note: when posting json via the request body to a coldfusion page, we must use this content type or we will get a 'IllegalArgumentException' on the ColdFusion processing page.
				type: "GET" //Note: for large payloads coming from the server, use the get method. The post method may fail as it is less efficient.
			}
		} //...transport:
	});//...var bestLanguageDs...

	// ---------------------------- Kendo dropdown. ----------------------------
	var serverSideLanguageDropdown = $("#serverSideLanguageDropdown").kendoDropDownList({
		optionLabel: "Select...",
		autoBind: false,
		dataTextField: "label",
		dataValueField: "value",
		filter: "contains",
		dataSource: bestLanguageDs,
	}).data("kendoDropDownList");
</script>

Download the CfJson component for ColdFusion (if you don't already have it)

In order to query a database and return the data as a JSON object to the client, we need to use ColdFusion on the server-side with a custom CFJson component. If you want to follow along, you can download this component from GitHub at https://github.com/GregoryAlexander77/CfJson.

Of course, we also need to use jQuery for the Ajax operations, but Kendo UI for jQuery requires jQuery so this should not be an issue.

Create a server-side function to use as the ColdFusion endpoint.

This function will query the database and return the data as a JSON object back to the client. The following function will be placed in the Demo.cfc component that I use for demonstration purposes. The function access argument must be remote when performing Ajax operations. Note the returnFormat="json" argument. This must be set to json, otherwise, the function will return plain text or WDDX and the client-side Ajax will fail.

Note: I don't exactly have a 'ServerLanguage' table anywhere in a database, so in this example, I will mimic a query object by building it in code.

<cffunction name="getBestLanguage" access="remote" returnformat="json" output="true"
	hint="Returns a JSON object back to the client to populate a Kendo dropdown. This function does not take any arguments">

	<!--- Create a ColdFusion query using CFScript. --->
	<cfscript>
		serverLanguage = queryNew("label,value","varchar,varchar", 
			[ 
				 {label="Adobe ColdFusion",value="ACF"}, 
				 {label="Lucee", value="Lucee"}
			]); 
	</cfscript>

	<!--- Now convert the query object into JSON using the convertCfQuery2JsonStruct in the CFJson.cfc and pass in the 'serverLanguage' query --->
	<cfinvoke component="#application.cfJsonComponentPath#" method="convertCfQuery2JsonStruct" returnvariable="jsonString" >
		<cfinvokeargument name="queryObj" value="#serverLanguage#">
		<cfinvokeargument name="contentType" value="json">
		<cfinvokeargument name="includeTotal" value="false">
		<!--- Don't include the data handle for Kendo dropdowns --->
		<cfinvokeargument name="includeDataHandle" value="false">
		<cfinvokeargument name="dataHandleName" value="">
		<!--- Set to true to force the column names into lower case. --->
		<cfinvokeargument name="convertColumnNamesToLowerCase" value="false">
	</cfinvoke>

	<cfreturn jsonString>

</cffunction>


Further Reading

This entry was posted on July 13, 2022 at 10:04 PM and has received 150 views.

Convert a ColdFusion Query into a JSON Object


I have used various HTML5 widgets extensively for the last decade and will share the functions that I use to convert ColdFusion objects returned from a database into JSON. In this article, I will highlight some of the logic in the code and show you how to use this for a variety of use cases.

 

What Does This Component Do?

These functions are a critical component of all of my HTML5 applications. These functions will convert both a ColdFusion query as well as a ColdFusion ORM array object into JSON that is passed back to the calling Ajax function. Unlike ColdFusion's native JSON functions, this will return the column names in the proper case rather than returning the column names in uppercase.

The function was originally created by Adrian Moreno. I modified this function nearly a decade ago and has been in use in several production environments over the last 10 years.  It is also used extensively in my open-source Galaxie Blog which is an HTML5 application. These two functions have been tested thoroughly and have handled server-side data operations on nearly every jQuery-based HTML5 widget that I have used. 

There may be something in the CF world that is a little more modern than this, however, I have written these functions to handle all of the common use cases that I have found when preparing JSON data. For example, some widgets want a data handle in the JSON, while others don't. These functions have handled all use cases that I have thrown at them when using Kendo UI and have handled other HTML5 libraries, such as jsGrid. I have tested a dozen different similar functions and this approach offered the best performance.

I am also using this component extensively in my how-to ColdFusion and Kendo blog series. If you are working with Kendo UI while reading this series, please download this component.


Download the CFJson Component from GitHub

This component can be found on GitHub at https://github.com/GregoryAlexander77/CfJson. It has been tested on ColdFusion 10 all the way through the most modern ColdFusion version, CF2021.


Working with ColdFusion Queries

When dealing with a native ColdFusion query object, use the convertCfQuery2JsonStruct function to convert it into JSON. This function takes a native ColdFusion query object and converts it into JSON.

There are several arguments, the queryObj is required, and the rest of the arguments are optional. 


Function Arguments

  • queryObj (required)
    Pass in the name of the ColdFusion query. This must be a ColdFusion query object.

  • contentType 
    Default value: json
    This argument, for now, is always set to json. This argument was put into the function as eventually, I hope to add JSONP support. 

  • includeDataHandleName
    Default value: false
    This inserts a data handle in front of the JSON. It is used for special use cases when using the Kendo widgets, and also is used for other HTML libraries, such as jsGrid.

  • dataHandleName
    Default value: false
    Specify your data handle name when setting the includeDataHandle argument to true.

  • includeTotal 
    Default value: false
    Used for the Kendo Grids and for Pagination.

  • overRideTotal
    Default value: false
    Used for pagination when filtering records in a Kendo Grid. 
  • newTotal
    Default value: false
    Used to indicate the total number of records after applying filters to a Kendo Grid. 

  • removeStringHtmlFormatting
    Default value: false
    Removes HTML and special characters in the JSON. This is used to create a sanitized string that is displayed in a grid (works with both Kendo and jsGrid grids)

  • columnThatContainsHtmlStrings
    default value: empty string
    When removeStringHtmlFormatting is set to true, specify the column that you want to be sanitized.

  • convertColumnNamesToLowerCase
    Default value: empty string
    This argument is rarely used. In certain situations, you may want to force all of the JSON element names to a lower case to avoid any case sensitivity issues.

Example ColdFusion Query

Here is a simple query that grabs the Galaxie Blog built-in roles. Here we are getting the role Id, name, and description and output this to JSON to populate our HTML5 widgets.

After obtaining the data, we will pass the name of this query, Data, to the convertCfQuery2JsonStruct method to convert this ColdFusion query into a JSON string. This query will be used for all of the examples below using the convertCfQuery2JsonStruct method that converts a ColdFusion query object into a JSON object.

Note: I am only getting the top two records to make it easier to view the JSON output below.

<!--- Make the query --->
<cfquery name="Data" datasource="#dsn#">
	SELECT TOP 2 RoleId
			,RoleName
			,Description
		FROM Role
</cfquery>

Common Usage Using Default Settings

This takes the ColdFusion query object that we just made, in this case, "Data", and it converts it into JSON without a total, data handle or any special formatting.

This particular usage supports most of the Kendo Widgets, other than the Kendo Grid or other specialized use cases. All of the other arguments are set at default and are not used. 

<!--- Convert the query object into JSON using the default parameters of convertCfQuery2JsonStruct method --->
<cfinvoke component="#application.cfJsonComponentPath#" method="convertCfQuery2JsonStruct" returnvariable="jsonString" >
	<cfinvokeargument name="queryObj" value="#Data#">
</cfinvoke>

This is what the JSON that the function returns when using the default arguments:

[
   {
      "Description":"All functionality.",
      "RoleId":1,
      "RoleName":"Administrator"
   },
   {
      "Description":"Can create and edit their own posts.",
      "RoleId":2,
      "RoleName":"Author"
   }
]

Getting the JSON with a Data Handle

If you need to use a data handle, use the following arguments:

<!--- Convert the query object into JSON --->
<cfinvoke component="cfJson" method="convertCfQuery2JsonStruct" returnvariable="jsonString" >
	<cfinvokeargument name="queryObj" value="#Data#">
	<cfinvokeargument name="contentType" value="json">
	<cfinvokeargument name="includeTotal" value="false">
	<!--- Don't include the data handle for Kendo grids ---> 
	<cfinvokeargument name="includeDataHandle" value="true">
	<cfinvokeargument name="dataHandleName" value="myData">
	<!--- Set to true to force the column names into lower case. --->
	<cfinvokeargument name="convertColumnNamesToLowerCase" value="false">
</cfinvoke>

Using this Function with a ColdFusion ORM Array

This component also works when using ColdFusion ORM. When you query a database using ColdFusion ORM using the map keyword in the query, the object returned is typically an object in an array of structures. 


Function Output

Here is what the function returns:

[ 
   {
      "blogurl":"https://www.gregoryalexander.com/blog/",
      "blogdescription":"A technical blog powered by Galaxie Blog - the most beautiful and functional open source ColdFusion/Kendo UI based blog in the world.",
      "blog":"Gregory's Blog"
   }
]

I will cover the other less common use cases in future blog posts as needed when I discuss the Kendo UI widgets.


Further Reading

This entry was posted on July 12, 2022 at 11:40 PM and has received 216 views.




Footer Logo

Your input and contributions are welcomed!

If you have an idea, BlogCfc based code, or a theme that you have built using this site that you want to share, please contribute by making a post here or share it by contacting us! This community can only thrive if we continue to work together.

Images and Photography:

Gregory Alexander either owns the copyright, or has the rights to use, all images and photographs on the site. If an image is not part of the "Galaxie Blog" open sourced distribution package, and instead is part of a personal blog post or a comment, please contact us and the author of the post or comment to obtain permission if you would like to use a personal image or photograph found on this site.

Credits:

Portions of Galaxie Blog are powered on the server side by BlogCfc, an open source blog developed by Raymond Camden. Revitalizing BlogCfc was a part of my orginal inspiration that prompted me to design this site.

Version:

Galaxie Blog Version 3.0 (Toby's Edition) June 14th 2022 Bahama Bank theme