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
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • How To Approach Java, Databases, and SQL [Video]
  • React, Angular, and Vue.js: What’s the Technical Difference?
  • An Overview of Programming Languages
  • Ultimate Guide to FaceIO

Trending

  • AI-Based Threat Detection in Cloud Security
  • Unmasking Entity-Based Data Masking: Best Practices 2025
  • Docker Base Images Demystified: A Practical Guide
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  1. DZone
  2. Coding
  3. Languages
  4. Embed a Spreadsheet Into Your Web App

Embed a Spreadsheet Into Your Web App

A developer gives a tutorial on how to embed a Keikai spreadsheet into your web application that allows users to interact with and download the spreadsheet.

By 
Hawk Chen user avatar
Hawk Chen
DZone Core CORE ·
Updated Jan. 08, 21 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
13.7K Views

Join the DZone community and get the full member experience.

Join For Free

Having an Embedded Spreadsheet Is Handy

Spreadsheets are used everywhere. There's no doubt that a spreadsheet application like Excel is one of the most popular business applications in the world. It is so easy that anyone can use it, yet it is so powerful that it enables experts in different fields to “code” their business logic in the simplest way possible.

A standalone spreadsheet is great for personal use, but it's hard to share. That is why Google Sheets was born. With Google Sheets, it becomes much easier to share and collaborate. But people want more — they want to have the sheet directly on their web pages; whether it is a web application, a wiki page, or a company website.

An easy way, like what Google Sheets offers, is to use an iframe to embed a spreadsheet into your web page. With a single iframe tag, you can easily display a spreadsheet on your web page. This is convenient if you wish to just “display” a sheet – but if your web page needs to interact with the spreadsheet, then iframe becomes a barrier you need to cross.

Here, I’d like to introduce an example embedding a Keikai spreadsheet into any web page and interact with your page, no matter on which platform your web application is based, e.g. Node.js, Python, or PHP.

The Story

Assume you have an existing web application managing the inventory of your products, and now you are adding a new feature called "Stock Search." Users can input some criteria to search for in-stock goods from a database, and the application will show the result in a spreadsheet. Then users can edit the list and process it as needed. For example, a user can search for low-stock goods and edit and export the search result as an XLSX or PDF file and send it to the vendor to place an order. Also when the user clicks on a specific good, it will show the vendor's contact information on the same row.

Prerequisite

In this article, I will focus on "embedding" a Keikai spreadsheet in an external app, and "the interaction" between the spreadsheet and the external page. Hence, I will assume the Keikai application is already built for "embedding" and "interaction." The external web page can be served by any non-Java server, e.g. a Node.js server.

For those who are interested in learning to build a Keikai application, please read the following articles:

  • Build a Web Application Based on Your Excel Files
  • Turn Your Excel File Into a Web Application

Architecture

The image below shows the relationship between the two applications and how the process of embedding Keikai works. Under this architecture, you still need to implement spreadsheet-related application logic in Java instead of other programming languages.

The process is:

  1. A browser requests an HTML page of the stock search app.
  2. The HTML page requests a zul file of the Keikai-based app.
  3. Download Keikai-related JavaScript and render Keikai on the page.

Page Layout

I designed the page with the Bootstrap grid system and divided the page into rows.

HTML
 




xxxxxxxxxx
1
18


 
1
<div class="container">
2
    <div class="row py-3">
3
    <!-- title -->
4
    </div>
5
    <div class="row justify-content-center py-1">
6
    <!-- search criteria input -->
7
    </div>
8
    <div id="embed">
9
   
10
    </div>
11
    <div class="row justify-content-end py-3 px-3">
12
    <!-- export button -->
13
    </div>
14
    <div class="row py-1 px-3">
15
    <!-- vender contact -->
16
    </div>
17
</div>


Only the spreadsheet is embedded with Keikai at <div id="embed">, so it's empty. Other HTML elements are already created on this page.

How to Embed

Keikai is based on the ZK UI framework, which supports a feature to embed any zul page into HTML via a JavaScript API. Here, I briefly describe the major steps. For details, please refer to ZK Developer's Reference Guide.

ZK embedded.js

First, you need to load a JavaScript API for embedding the Keikai-based application from a URL:

HTML
 




xxxxxxxxxx
1


 
1
<script id="embeddedScript" src="http://keikai-app/zkau/web/js/zkmax/embedded/embedded.js" />


keikai-app is just an example – you need to replace it with your Keikai application context root.

This JavaScript defines the zEmbedded API so that I can use it to embed a zul file.

CORS

Because the stock search application is normally in a different domain from the Keikai-based application, you need to set the HTTP-header to allow Keikai resources to be loaded by a cross-origin request.

You need to set at least the following CORS headers:

Plain Text
 




x


 
1
Access-Control-Allow-Origin: [allowed embedding origins]
2
Access-Control-Allow-Headers: zk-sid
3
Access-Control-Expose-Headers: zk-sid, zk-error
4
Access-Control-Allow-Credentials: true
5
Access-Control-Allow-Methods: GET, POST


You can reference CorsHeaderFilter to set these headers.

Ref: MDN: Cross-Origin Resource Sharing (CORS)

The API to Embed

Then, call the ZK embedding API, zEmbedded.load(), to embed zul into the HTML page like:

JavaScript
x
 
1
zEmbedded.load('embed', 'http://keikai-app/useCase/stock-search.zul')
2
  .then(function(result) {
3
    zk.log('keikai is embedded!' + result.widget.uuid); //result contains the first widget
4
  }).catch(reason => {
5
    alert('ZK mounting error: ' + reason);
6
});

The URL, http://keikai-app/useCase/stock-search.zul, is the same URL as the one you visit with a browser.

Anchor Element

The first parameter, embed, is the anchor element ID that Keikai will render itself in. It's just a <div id="embed"> element in my example.

Communication Between a Web Page and Keikai

After embedding Keikai, you can see the file rendered in the HTML page, and you can edit and scroll. But it still lacks communication between the HTML page and Keikai itself. I will explain how to do this by implementing "search product" and "show vendor contact information."

Java Controller

There is a Java controller applied on Keikai at the server-side. If you check search-stock.zul, you will see a class name at apply:

XML
 




xxxxxxxxxx
1


 
1
<spreadsheet ... apply="io.keikai.devref.usecase.embed.StockSearchComposer"/>


The controller can receive the events from the client-side, query products from a database, and contain event listeners with application logic like exporting and accessing cell values.

Fire Events to the Server

ZK framework supports the event-driven programming model. So the main way to communicate with the Keikai controller is to fire an event in JavaScript. The Keikai spreadsheet will create a JavaScript widget in the browser, and it provides the function fire() to fire events to the Keikai Java controller.

Export XLSX and PDF

I have defined the event name, onExportExcel, in the Java controller. So, what I now have to do is to fire the event at the client-side to invoke the event listener of the Java controller:

JavaScript
 




xxxxxxxxxx
1


 
1
$('#exprtExcel').on('click', () => {
2
    Controller.fireEvent('onExportExcel', null);
3
});


The ZK framework contains jQuery by default, so you can use $('#exprtExcel').on() to register an onclick listener on the button <button id="exprtExcel" class="btn btn-primary">Export to Excel</button>.

XLSX Exporter

In the server-side controller, I implement exporting with Keikai's Exporter in a few lines.

Java
 




xxxxxxxxxx
1
10


 
1
private Exporter exporter = Exporters.getExporter();
2
 
          
3
    @Listen("onExportExcel = spreadsheet")
4
    public void exportExcel() throws IOException {
5
        File file = File.createTempFile(Long.toString(System.currentTimeMillis()), "temp");
6
        try (FileOutputStream fos = new FileOutputStream(file);) {
7
            exporter.export(spreadsheet.getBook(), fos);
8
        }
9
        Filedownload.save(new AMedia(spreadsheet.getBook().getBookName(), "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", file, true));
10
    }


Exporting a PDF is quite similar, please check the source code on GitHub.

JavaScript Controller on the HTML Page

To avoid a potential function name conflict, I put the event firing functions into a JavaScript class called Controller:

JavaScript
 




xxxxxxxxxx
1
11


 
1
class Controller{
2
    /** fire an event to invoke an event listener at the server-side */
3
    static fireEvent(eventName, parameter){
4
         Controller.getSpreadsheet().fire(eventName, parameter, {toServer:true});
5
    }
6
 
          
7
    /** get keikai spreadsheet js widget by ID selector "$id" */
8
    static getSpreadsheet(){
9
        return zk.Widget.$('$spreadsheet');
10
    };


ZK Widget Selector

zk.Widget.$(mySelector) is a function that can get a ZK widget reference by a selector syntax. The ID spreadsheet is the value specified in the "id" attribute of <spreadsheet> in stock-search.zul.

Search Products With Criteria

When a user clicks the "Search" button, this application can show products in the sheet based on the criteria.

Firing an Event at Client-side

You can follow a similar pattern to invoke the onSearch event listener, but this time I need to pass search criteria as the second parameter.

JavaScript
 




x


 
1
$('#search').on('click', () => {
2
    Controller.fireEvent('onSearch', Controller.getFilterCriteria());
3
});


Extract user input as a JSON object:

JavaScript
 




xxxxxxxxxx
1


 
1
class Controller{
2
    ...
3
 
          
4
    static getFilterCriteria(){
5
        return {"category": $('#category').val(),
6
                "min": $('#min').val(),
7
                "max": $('#max').val()};
8
    }


Receive the Parameter

Through event.getData(), I can get a JSON object sent from the client-side and convert it to FilterCriteria:

Java
 




xxxxxxxxxx
1


 
1
    @Listen("onSearch = spreadsheet")
2
    public void search(Event event) {
3
        FilterCriteria criteria = convertCriteria((JSONObject) event.getData());
4
        List<Product> result = productService.query(criteria);
5
        populateResult(result);
6
    }


  • Line 1: The annotation to register an onSearch event listener on the component spreadsheet.
  • Line 4: The productService just represents a business layer class – it can return a list of products upon given criteria. You can imagine that class as any of your business classes, just to perform a business operation.

Populate the Result

To populate the product list into the sheet on the browser, you need to call an important API Range. There are two basic steps:

  1. Create a Range object that represents one or multiple cells, e.g. Ranges.rangeByName(resultSheet, "ReportTable")
  2. Call Range methods to get or set data on that range, e.g. setCellValues()
Java
 




xxxxxxxxxx
1
12


 
1
    /**
2
     * populate search result into a sheet
3
     */
4
    private void populateResult(List<Product> result) {
5
        ...
6
        //fill searched products
7
        Range currentRow = Ranges.rangeByName(resultSheet, "ReportTable").toCellRange(0, 0).toRowRange(); //start from the first row
8
        for (Product p : result) {
9
            currentRow.setCellValues(p.getId(), p.getCategory(), p.getName(), p.getVendor(), p.getQuantity(), p.getPrice());
10
            currentRow = currentRow.toShiftedRange(1, 0);
11
        }
12
        ...


After invoking the event listener, Keikai will communicate with the client-side widgets to render cell values on the sheet. I don't need to take care of communication and rendering details.

For more APIs, please refer to the Keikai Developer Reference Guide.

Show a Vendor Contact

When a user clicks on a cell, it will show the vendor contact information at the bottom of the page from the server-side. This demonstrates how a sheet interacts with a page (triggered inside a sheet).

Invoke JavaScript Functions

The Java method, Clients.evalJavaScript(), allows you to call a JavaScript function in a browser. That's how Java controllers call the client-side JavaScript controller's function, Controller.showVendor().

In this example, I assume the server has the vendor data. Therefore, I register anEvents.ON_CELL_CLICK listener that queries a vendor by its name. Then, every click on a cell invokes this method:

Java
 




xxxxxxxxxx
1
10


 
1
    @Listen(Events.ON_CELL_CLICK + " = spreadsheet")
2
    public void showVendor(CellMouseEvent event){
3
        //get vendor name
4
        Range vendorCell = RangeHelper.getTargetRange(event).toRowRange().toCellRange(0, 3);
5
        String name = vendorCell.getCellData().getStringValue();
6
        VendorService.Vendor vendor = VendorService.query(name);
7
        if (vendor != null){
8
            Clients.evalJavaScript("Controller.showVendor(" + gson.toJson(vendor) + ")");
9
        }
10
    }


  • Line 4: I locate the cell of the vendor name based on the row the user clicked and find its fourth column cell.
  • Line 5: Get the cell value as a string.
  • Line 8: Invoke a JavaScript function with a vendor JSON object as a parameter

Render the Vendor Contact

At the client-side, I simply just render the vendor contact on the page with jQuery:

JavaScript
 




xxxxxxxxxx
1
10


 
1
class Controller{
2
...
3
    /** render vendor info on the page
4
    */
5
    static showVendor(vendor){
6
        $('#name').text(vendor.name);
7
        $('#tel').text(vendor.tel);
8
        $('#email').text(vendor.email);
9
    }
10
}


Summary

To wrap up, I have demonstrated how you can embed a spreadsheet into any web page, and how the spreadsheet can interact with the web page in both directions. I hope this helps you bring sme spreadsheet power into your web applications.

Get the Source

The GitHub repository contains the runnable code so that you can try it by yourself.

Web application app JavaScript Database Event Java (programming language) HTML

Opinions expressed by DZone contributors are their own.

Related

  • How To Approach Java, Databases, and SQL [Video]
  • React, Angular, and Vue.js: What’s the Technical Difference?
  • An Overview of Programming Languages
  • Ultimate Guide to FaceIO

Partner Resources

×

Comments

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
  • [email protected]

Let's be friends: