DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • How Spring Boot Starters Integrate With Your Project
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Trending

  • Build an MCP Server Using Go to Connect AI Agents With Databases
  • Failure Handling Mechanisms in Microservices and Their Importance
  • My LLM Journey as a Software Engineer Exploring a New Domain
  • Solid Testing Strategies for Salesforce Releases
  1. DZone
  2. Coding
  3. Frameworks
  4. AJAX With CKEditor in Spring Boot

AJAX With CKEditor in Spring Boot

Learn how to load data with GET requests and object ids, set the data to a CKEditor instance, and save the CKEditor's data back to your database with a POST request.

By 
Michael Good user avatar
Michael Good
·
Updated Nov. 21, 17 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
14.7K Views

Join the DZone community and get the full member experience.

Join For Free

In this article, we will cover how to use CKEditor with Spring Boot. In this tutorial, we will be importing an XML document with plenty of data, program the ability to load a set of data to the CKEditor instance with a GET request, and do a POST request to save the CKEditor's data.

Technologies we will be using include MongoDB, Thymeleaf, and Spring Batch.

The full source code for this tutorial is available on GitHub.

What Is CKEditor?

CKEditor is a browser-based What-You-See-Is-What-You-Get (WYSIWYG) content editor. CKEditor aims to bring to a web interface common word processor features found in desktop editing applications like Microsoft Word and OpenOffice.

CKEditor has numerous features for end users in regards to the user interface, inserting content, authoring content, and more.

There are different versions of CKEditor, but for this tutorial, we are using CKEditor 4. To see a demo, visit: https://ckeditor.com/ckeditor-4/

The XML Document

As mentioned, we are uploading an XML document in this application. The XML data will be inserted into the database and used for the rest of the tutorial:

<?xml version="1.0"?>
<Music xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="MUS-1" style="1.1">
    <status date="2017-11-07">draft</status>
    <title xmlns:xhtml="http://www.w3.org/1999/xhtml">Guide to Music I Like - No Specific Genre</title>
    <description xmlns:xhtml="http://www.w3.org/1999/xhtml">This guide presents a catalog of music that can be found on Spotify. 
    <html:br xmlns:html="http://www.w3.org/1999/xhtml" /><html:br xmlns:html="http://www.w3.org/1999/xhtml" />
    This is a very small sample of music found on Spotify and is no way to be considered comprehensive.
    </description>
    <songs>
        <song>
            <artist>
    Run the Jewels
    </artist>
            <song-title>Legend Has It</song-title>
        </song>
        <song>
            <artist>
    Kendrick Lamar
    </artist>
            <song-title>ELEMENT.</song-title>
        </song>
        <song>
            <artist>
    Weird Al Yankovic
    </artist>
            <song-title>NOW That's What I Call Polka!</song-title>
        </song>
        <song>
            <artist>
    Eiffel 65
    </artist>
            <song-title>Blue (Da Ba Dee) - DJ Ponte Ice Pop Radio</song-title>
        </song>
        <song>
            <artist>
    YTCracker
    </artist>
            <song-title>Hacker Music</song-title>
        </song>
        <song>
            <artist>
    MAN WITH A MISSION
    </artist>
            <song-title>
    Raise Your Flag
    </song-title>
        </song>
        <song>
            <artist>
    GZA, Method Man
    </artist>
            <song-title>
    Shadowboxin'
    </song-title>
        </song>
    </songs>
</Music>


Model

For the above XML code, we can model a Song like this:

public class SongModel {
    @Id
    private String id;
    @Indexed
    private String artist;
    @Indexed
    private String songTitle;
    @Indexed
    private Boolean updated;

    public Boolean getUpdated() {
        return updated;
    }
    public void setUpdated(Boolean updated) {
        this.updated = updated;
    }
    public String getArtist() {
        return artist;
    }
    public void setArtist(String artist) {
        this.artist = artist;
    }
    public String getSongTitle() {
        return songTitle;
    }
    public void setSongTitle(String songTitle) {
        this.songTitle = songTitle;
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    @JsonCreator
    public SongModel(
        @JsonProperty("artist") String artist,
        @JsonProperty("song-title") String songTitle) {
        this.artist = artist;
        this.songTitle = songTitle;
    }

    @Override
    public String toString() {
        return "Person [id=" + id + ", artist=" + artist + ", song-title=" + songTitle + "]";
    }

}


For our application, we will be differentiating between an unmodified song and a song that has been modified in CKEditor with a separate Model and Repository.

Let's now define what an Updated Song is:

public class UpdatedSong {

    @Id
    private String id;
    @Indexed
    private String artist;
    @Indexed
    private String songTitle;
    @Indexed
    private String html;
    @Indexed
    private String sid;

    public String getSid() {
        return sid;
    }
    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getArtist() {
        return artist;
    }
    public void setArtist(String artist) {
        this.artist = artist;
    }
    public String getSongTitle() {
        return songTitle;
    }
    public void setSongTitle(String songTitle) {
        this.songTitle = songTitle;
    }
    public String getHtml() {
        return html;
    }
    public void setHtml(String html) {
        this.html = html;
    }

}


File Upload and Processing

As this article's focus is on CKEditor and AJAX, we aren't going to go into detail on the file upload and processing with Spring Batch. We have reviewed this process in depth in these prior posts, however:

  • Spring Batch CSV Processing
  • Converting XML to JSON + Raw Use in MongoDB + Spring Batch

Setting Data to CKEditor: GET Request

It is our goal to retrieve the data for an individual song and display that data in CKEditor. There are two issues to tackle: retrieving the data for an individual song and displaying it in CKEditor.

Client Side Code

In view.html, we use a table in Thymeleaf to iterate through each Song in the Song repository. To be able to retrieve the data from the server for a specific song, we pass in the Song's id to a function.

Here's the snippet of code that is responsible for calling the function that retrieves the data from the server and subsequently sets the data to the CKEditor instance:

<table class="table datatable">
<thead>
<tr>
<th>Artist</th>
<th>Song Title</th>
<th>Load</th>

</tr>
</thead>
<tbody>
<tr th:each="songList : ${songList}">
<td th:text="${songList.artist}">Text ...</td>
<td th:text="${songList.songTitle}">Text ...</td>
<td><button th:onclick="|getSong('${songList.id}')|"
id="button" class="btn btn-primary btn-condensed">
<i class="glyphicon glyphicon-folder-open"></i>
</button></td>
</tr>
</tbody>
</table>


As we can see, the id of Song is essential for us to be able to retrieve the data.

In the getSong function, we use a deferred promise to ensure that data is set after the GET request:

function getSong(song) {
    $.ajax({
        url : "/api/show/?sid=" + song,
        type : 'GET',
        dataType : 'text'
    }).then(function(data) {
        var length = data.length-2;
        var datacut = data.slice(9,length);
        CKEDITOR.instances.content.setData(datacut);

    });

    $("#form").attr("action", "/api/save/?sid=" + song);

};


Server Side Code

getSong accepts a parameter named sid, which stands for Song id. sid is also a path variable in the @GetMapping. We treat sid as a String because this is the id of the Song from MongoDB.

We check if the Song has been modified and if so we retrieve the associated UpdatedSong entity. If not, we treat the Song differently. Ultimately, we return a simple POJO with a String for data named ResponseModel, however:

    @GetMapping(value={"/show/","/show/{sid}"})
    public ResponseEntity<?> getSong(@RequestParam String sid, Model model){
        ResponseModel response = new ResponseModel();
        System.out.println("SID :::::" + sid);
        ArrayList<String> musicText = new ArrayList<String>();
        if(sid!=null){
            String sidString = sid;
            SongModel songModel = songDAO.findOne(sidString);
            System.out.println("get status of boolean during get ::::::" + songModel.getUpdated());
            if(songModel.getUpdated()==false ){

                musicText.add(songModel.getArtist());
                musicText.add(songModel.getSongTitle());
                String filterText = format.changeJsonToHTML(musicText);
                System.out.println("formatted song text ::::::::" + filterText);
                response.setData(filterText);

            } else if(songModel.getUpdated()==true){
                UpdatedSong updated = updatedDAO.findBysid(sidString);
                String text = updated.getHtml();
                System.out.println("getting the updated text ::::::::" + text);
                response.setData(text);
            }

        }

        model.addAttribute("response", response);

        return ResponseEntity.ok(response);
    }


ResponseModel is a very simple POJO as mentioned:

public class ResponseModel {
    private String data;

    public ResponseModel(){

    }

    public ResponseModel(String data){
            this.data = data;
    }

    public String getData() {
            return data;
    }

    public void setData(String data) {
            this.data = data;
    }
}


Saving CKEditor Data: POST Request

POSTing the data isn't much of a challenge; however, ensuring that the data is handled appropriately can be.

Client Side Code

As the CKEditor instance is a textarea within a form, we can trigger a function on a form submit:

$(document)
    .ready(
        function() {

            // SUBMIT FORM
            $("#form").submit(function(event) {
                // Prevent the form from submitting via the browser.
                event.preventDefault();
                ajaxPost();
            });


ajaxPost() retrieves the current data in the CKEditor and sets it to the variable formData:

function ajaxPost() {

// PREPARE FORM DATA
var formData = CKEDITOR.instances.content
    .getData();

// DO POST
$
    .ajax({
            type: "POST",
            contentType: "text/html",
            url: $("#form").attr("action"),
            data: formData,
            dataType: 'text',
            success: function(result) {

                $("#postResultDiv")
                    .html(
                        "

                        " + "Post Successfully! " + "

                        ");

                        console.log(result);
                    },
                    error: function(e) {
                        alert("Error!")
                        console.log("ERROR: ", e);
                    }
            });

    }

})


It is important to note:

  • contentType is "text/html"
  • dataType is "text"

Having the incorrect contentType or dataType can lead to errors or malformed data.

Server Side Code

We stated in our contentType for the POST request that the mediatype is "text/html".  We need to specify in our mapping that this will be consumed. Therefore, we add consumes = MediaType.TEXT_HTML_VALUE with our @PostMapping.Areas for us to note include:

  • @RequestBody String body is responsible for setting the variable body to the content of our request
  • We once again return ResponseModel, the simple POJO described earlier that contains our data
  • We treat a previously modified SongModel different than one that has not been modified before

Also, like the GET request, the sid allows us to deal with the correct Song:

@PostMapping(value={"/save/","/save/[sid]"}, consumes = MediaType.TEXT_HTML_VALUE)
   public @ResponseBody ResponseModel saveSong( @RequestBody String body, @RequestParam String sid){
       ResponseModel response = new ResponseModel();
       response.setData(body);
       SongModel oldSong = songDAO.findOne(sid);
       String songTitle = oldSong.getSongTitle();
       String artistName = oldSong.getArtist();
       if(oldSong.getUpdated() == false){
           UpdatedSong updatedSong = new UpdatedSong();
           updatedSong.setArtist(artistName);
           updatedSong.setSongTitle(songTitle);
           updatedSong.setHtml(body);
           updatedSong.setSid(sid);
           oldSong.setUpdated(true);
           songDAO.save(oldSong);
           updatedDAO.insert(updatedSong);
           System.out.println("get status of boolean during post :::::" + oldSong.getUpdated());
       }else{
           UpdatedSong currentSong = updatedDAO.findBysid(sid);
           currentSong.setHtml(body);
           updatedDAO.save(currentSong);
       }        

       return response;
   }


Demo

We visit localhost:8080:

Upload form for ckeditor spring boot application

We upload the provided music-example.xml file:

modal result spring boot ckeditor application

We click “Load” for a song:

get request for ckeditor spring boot application

We add content and click “Save”:

post request for spring boot ckeditor application

Conclusion

In this tutorial, we covered how to load data using a GET request with the object's id, set the data to the CKEditor instance, and save the CKEditor's data back to the database with a POST request. There's extra code, such as using two different entities for the data (an original and a modified version), that isn't necessary, but hopefully is instructive.

The full code can be found on GitHub.

CKEditor Spring Framework AJAX Data (computing) Spring Boot Requests

Published at DZone with permission of Michael Good, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Boot: Cross-Origin AJAX HTTP Requests
  • How Spring Boot Starters Integrate With Your Project
  • Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl
  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!