<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hawk Chen</title>
    <description>The latest articles on DEV Community by Hawk Chen (@hawkchen).</description>
    <link>https://dev.to/hawkchen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F333866%2Fc23b4fef-f9d0-4cac-a22a-8de51e3359c6.png</url>
      <title>DEV Community: Hawk Chen</title>
      <link>https://dev.to/hawkchen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hawkchen"/>
    <language>en</language>
    <item>
      <title>Safari's Tab Key Trap: Why Your Website's Focus Navigation Might Be Breaking for Safari Users</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Thu, 22 May 2025 04:10:23 +0000</pubDate>
      <link>https://dev.to/hawkchen/safaris-tab-key-trap-why-your-websites-focus-navigation-might-be-breaking-for-safari-users-5632</link>
      <guid>https://dev.to/hawkchen/safaris-tab-key-trap-why-your-websites-focus-navigation-might-be-breaking-for-safari-users-5632</guid>
      <description>&lt;p&gt;Recently, a &lt;a href="https://www.zkoss.org/" rel="noopener noreferrer"&gt;zk&lt;/a&gt; user reported that they couldn't tab through all interactive elements on a zul page. This behavior is tied to a little-known setting deep in Safari’s Settings.&lt;/p&gt;

&lt;p&gt;Keyboard navigation is essential for accessibility, allowing users who rely on keyboards or assistive technologies to navigate without a mouse. It also benefits power users who prefer using Tab for faster navigation. If this doesn't work as expected, especially on Safari — one of the major browsers — it can be a frustrating and invisible blocker.&lt;/p&gt;

&lt;p&gt;Assuming I create a page with a textbox, menubar, menuitem, anchor link, intbox, navbar, and navitem, and visit it with various browsers.&lt;/p&gt;

&lt;p&gt;When pressing the &lt;code&gt;Tab&lt;/code&gt; key in &lt;strong&gt;Chrome&lt;/strong&gt; (136.0), &lt;strong&gt;Firefox&lt;/strong&gt; (138.0), and &lt;strong&gt;Edge&lt;/strong&gt; (136.0), I can successfully move through all components, including each link, menu item, and nav item.&lt;/p&gt;

&lt;p&gt;But within &lt;strong&gt;Safari&lt;/strong&gt; (18.4), I can only focus on some components. Safari will skip most menu items, links, and nav items.&lt;/p&gt;

&lt;p&gt;You can try &lt;a href="https://zkfiddle.org/sample/1e63gvb/2-focus-keyboard-navigation-accessibility-safari" rel="noopener noreferrer"&gt;the page on zk fiddle&lt;/a&gt; with your Safari.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo53i6ynluoi0nz47347z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo53i6ynluoi0nz47347z.gif" alt="Safari doesn't focus on some components" width="408" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found the root cause is:&lt;/p&gt;

&lt;p&gt;The option “Press Tab to highlight each item on a webpage”  in Safari Settings is unchecked by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov5hx9l2v9u11yy8kvld.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov5hx9l2v9u11yy8kvld.png" alt="Safari default Settings" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After I checked that option, I can focus on all components by pressing the &lt;code&gt;Tab&lt;/code&gt; key. If it’s unchecked, you have to press &lt;code&gt;Option&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt; key to focus on all components.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Give your classic spreadsheet a modern touch</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Thu, 04 Mar 2021 03:46:05 +0000</pubDate>
      <link>https://dev.to/hawkchen/give-your-classic-spreadsheet-a-modern-touch-58dg</link>
      <guid>https://dev.to/hawkchen/give-your-classic-spreadsheet-a-modern-touch-58dg</guid>
      <description>&lt;p&gt;Spreadsheets are no doubt the most popular business application – they are heavily used in all fields, favored by users from finance experts in Wall Street to top scientists in NASA. Spreadsheets allow users to calculate, organize and store data, and most important of all, analyze data without having to program.&lt;/p&gt;

&lt;p&gt;Nowadays spreadsheets extend their reach more – they no longer sit inside a user’s Excel desktop application, they are now widely used online, either being embedded inside applications or run on an online platform like google sheets.&lt;br&gt;
But when a spreadsheet goes online, can we do more than just duplicating Excel’s functionality into the browser?&lt;/p&gt;
&lt;h2&gt;
  
  
  More Than a Spreadsheet
&lt;/h2&gt;

&lt;p&gt;While spreadsheets are great for displaying tabular data, presenting forms or charts, it is not always great to display a large block of text or display images and other elements that have varied dimensions. &lt;br&gt;
This is where we can leverage today’s fancy and easy-to-use web UI components to work with an online spreadsheet and upgrade spreadsheet users’ experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e0txjrakowbwu5m8imu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e0txjrakowbwu5m8imu.gif" width="600" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here I am taking &lt;a href="https://keikai.io/" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt; spreadsheet and a &lt;a href="https://www.zkoss.org" rel="noopener noreferrer"&gt;ZK&lt;/a&gt; &lt;a href="https://www.zkoss.org/wiki/ZK_Component_Reference/Containers/Drawer" rel="noopener noreferrer"&gt;drawer component&lt;/a&gt; as an example demonstrating how this can be done. In this example, we will add a slide-in/out Help panel to our spreadsheet where we can display large blocks of Help text in a much more easy-to-read format. &lt;/p&gt;

&lt;p&gt;Let me show you how to build this page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Load the Form
&lt;/h3&gt;

&lt;p&gt;I can load my &lt;code&gt;helpTemplate.xlsx&lt;/code&gt; in a zul by specifying &lt;code&gt;src&lt;/code&gt; at &lt;code&gt;&amp;lt;spreadsheet/&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;formHelp.zul&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;spreadsheet&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ss"&lt;/span&gt; 
&lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"60%"&lt;/span&gt; 
&lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"margin: 0 auto;"&lt;/span&gt; 
&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/WEB-INF/books/helpTemplate.xlsx"&lt;/span&gt;
&lt;span class="na"&gt;maxVisibleRows=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;maxVisibleColumns=&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other attributes of the tag determine spreadsheet looking; please refer to &lt;a href="https://doc.keikai.io/dev-ref/Control_Components" rel="noopener noreferrer"&gt;Keikai Developer Reference&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Put a help page along with Keikai
&lt;/h3&gt;

&lt;p&gt;There are 5 blocks of numbers in the form above. Hence, I create an HTML page and put help descriptions into 5 different color boxes. Each box has a corresponding help box with the same background and title. So people who read the help page can easily identify which help text matches their needs for the current block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30u3elwz97n3h1lx7rqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30u3elwz97n3h1lx7rqq.png" width="161" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I use &lt;code&gt;&amp;lt;include&amp;gt;&lt;/code&gt; component to include that help HTML into &lt;a href="https://www.zkoss.org/wiki/ZK_Component_Reference/Containers/Drawer" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;drawer&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;spreadsheet&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;drawer&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"helpDrawer"&lt;/span&gt; &lt;span class="na"&gt;visible=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; 
&lt;span class="na"&gt;position=&lt;/span&gt;&lt;span class="s"&gt;"right"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"40%"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;include&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"help.html"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/drawer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;visible="false"&lt;/code&gt; means it's hidden by default, so you won't see the drawer at first. But users can click the "Help" cell to show the help.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note, this is just a simple example; you can definitely put any other HTML content (images, videos...) to the help page based on your own context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Allow Users to Show Help Page
&lt;/h3&gt;

&lt;p&gt;To show the help panel when a user clicks the Help button (cell), I need to add an event listener for the cell click event in a controller.&lt;br&gt;
Keikai supports the MVC pattern, so it's better to implement my application logic in a controller better OO design. The controller looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelpTextComposer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;SelectorComposer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Wire&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Drawer&lt;/span&gt; &lt;span class="n"&gt;helpDrawer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Listen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ON_CELL_CLICK&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" = spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showHelpText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CellMouseEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="nc"&gt;Range&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RangeHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTargetRange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCellValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; 
          &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCellValue&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Help"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;helpDrawer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.zkoss.org/javadoc/latest/zk/org/zkoss/zk/ui/select/SelectorComposer.html" rel="noopener noreferrer"&gt;&lt;code&gt;SelectorComposer&lt;/code&gt;&lt;/a&gt; helps me to get a reference of &lt;code&gt;helpDrawer&lt;/code&gt; which is instantiated by &lt;a href="https://www.zkoss.org" rel="noopener noreferrer"&gt;ZK framework&lt;/a&gt;. That's why I declare a variable of &lt;code&gt;Drawer&lt;/code&gt; without instantiating its object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Listen&lt;/code&gt; can register the method as an event listener for &lt;code&gt;Events.ON_CELL_CLICK&lt;/code&gt; of the spreadsheet. So when a user clicks a cell, ZK will invoke this method.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RangeHelper.getTargetRange(e)&lt;/code&gt; returns a &lt;code&gt;Range&lt;/code&gt; that represents the clicked cell. Then I can check the cell's value by &lt;code&gt;cell.getCellValue().toString()&lt;/code&gt; to know whether a user clicks the "Help" cell or not.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;helpDrawer.open()&lt;/code&gt; makes the drawer slide in.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Spreadsheets are brought online for easier sharing and collaboration. But we can do more! This example uses Keikai Spreadsheet and ZK components to demonstrate how you can leverage rich UI components to upgrade user’s spreadsheet experience while preserving its benefits. &lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;You can see the complete source code in &lt;a href="https://github.com/keikai/dev-ref/blob/master/src/main/webapp/useCase/formHelp.zul" rel="noopener noreferrer"&gt;Keikai developer reference repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spreadsheet</category>
      <category>web</category>
      <category>excel</category>
      <category>java</category>
    </item>
    <item>
      <title>Protect Formulas in a Web Spreadsheet Application</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Tue, 02 Jun 2020 04:47:58 +0000</pubDate>
      <link>https://dev.to/hawkchen/protect-formulas-in-a-web-spreadsheet-application-43j0</link>
      <guid>https://dev.to/hawkchen/protect-formulas-in-a-web-spreadsheet-application-43j0</guid>
      <description>&lt;h1&gt;
  
  
  The User Story
&lt;/h1&gt;

&lt;p&gt;One of our customers is a financial professional who manages financial risks for his clients. He develops a tool based on Excel to analyze the risk of investments and forecast the market trend. He creates many valuable formulas in his Excel file that help him do the job. &lt;/p&gt;

&lt;p&gt;Now he plans to turn this tool into a web application that allows users to input financial data and produces a risk analysis result. But he wants to hide his valuable formulas since that's his business secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5osuq874211ompag01q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5osuq874211ompag01q.png" alt="Imgur" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Overall Process
&lt;/h1&gt;

&lt;p&gt;We can implement his plan with &lt;a href="https://keikai.io" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt;. The overall idea of this system is like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpempxtn2vpu0tsckl049.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpempxtn2vpu0tsckl049.jpg" alt="Imgur" width="600" height="864"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, I create an xlsx file with 3 sheets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;source sheet&lt;/strong&gt;: accept user input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;transform sheet&lt;/strong&gt;: calculate results with formulas by referencing cells in the source sheet. To protect the valuable formulas, this sheet is &lt;strong&gt;hidden&lt;/strong&gt; from users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;display sheet&lt;/strong&gt;: display the calculation result to users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Import xlsx File
&lt;/h1&gt;

&lt;p&gt;By specifying the path of an xlsx file, I can import it into &lt;a href="https://keikai.io" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dataTransform.zul&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;spreadsheet&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/WEB-INF/books/transform.xlsx"&lt;/span&gt;
                 &lt;span class="na"&gt;maxVisibleColumns=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt; &lt;span class="na"&gt;maxVisibleRows=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
                 &lt;span class="na"&gt;apply=&lt;/span&gt;&lt;span class="s"&gt;"io.keikai.devref.usecase.DataTransformComposer"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Keikai supports MVC pattern, I can also apply a Controller &lt;code&gt;DataTransformComposer&lt;/code&gt; to manipulate Keikai and listen to events in Java API.&lt;/p&gt;

&lt;p&gt;This is the imported result in a browser:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7p1laoenqeof661i6j29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7p1laoenqeof661i6j29.png" alt="Imgur" width="564" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the screenshot, there is no sheet tab. &lt;a href="https://keikai.io" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt; allows you to hide the sheet tab. Hence, users can't switch to the sheet that contains the formulas.&lt;/p&gt;
&lt;h1&gt;
  
  
  Formulas Supported
&lt;/h1&gt;

&lt;p&gt;Keikai supports &lt;a href="https://doc.keikai.io/dev-ref/Supported_Formula_Functions" rel="noopener noreferrer"&gt;most of the Excel functions (over 250)&lt;/a&gt; and syntax. Keikai can import the formulas you write in Excel without any modification and they will work as they were.&lt;/p&gt;
&lt;h1&gt;
  
  
  Protect Sheets
&lt;/h1&gt;

&lt;p&gt;I don't want users to arbitrarily change my sheet, so I want to enable sheet protection. Before that, I need to create a &lt;code&gt;SheetProtection&lt;/code&gt; with proper permissions. There are 14 permissions to choose. Keikai provides a Builder pattern API, so I can just setup those permissions I care:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SheetProtection&lt;/span&gt; &lt;span class="no"&gt;VIEW_ONLY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SheetProtection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withSelectLockedCellsAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withSelectUnlockedCellsAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAutoFilterAllowed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I enable sheet protect in a loop with &lt;code&gt;SheetProtection&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;protectAllSheets&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBook&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getNumberOfSheets&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBook&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSheetAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;protectSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;VIEW_ONLY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Unlocked Cells
&lt;/h2&gt;

&lt;p&gt;Under the sheet protection, every cell is read-only. But I still need to accept user input in the source sheet, so I need to set several cells as unlocked in Excel to make them editable. Just open cell format / Protection, uncheck "Locked":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F487w9dk6hj331gz5fiwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F487w9dk6hj331gz5fiwd.png" alt="Imgur" width="619" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Listen to Cell Click
&lt;/h1&gt;

&lt;p&gt;There are 2 cells working as buttons. When users click them, Keikai will calculate and show the result. I have to implement this logic in my controller class by the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Listen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ON_CELL_CLICK&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"=spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onCellClick&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CellMouseEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sheetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSheet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSheetName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheetName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;SOURCE_SHEET&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColumn&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;simpleWorkflow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColumn&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;complexWorkflow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Listen(Events.ON_CELL_CLICK + "=spreadsheet")&lt;/code&gt; is to register an &lt;code&gt;ON_CELL_CLICK&lt;/code&gt; event listener, method &lt;code&gt;onCellClick()&lt;/code&gt;, for component &lt;code&gt;spreadsheet&lt;/code&gt;. Therefore, when a user clicks a cell, it will invoke &lt;code&gt;onCellClick()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In this listner method, I need to check clicked cell's sheet, row, and column to determine which workflow to perform.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Show Display Sheet
&lt;/h1&gt;

&lt;p&gt;Finally, I call &lt;code&gt;setSelectedSheet(SIMPLE_DISPLAY_SHEET)&lt;/code&gt; to show the display sheet to users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;simpleWorkflow&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SIMPLE_DISPLAY_SHEET&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Benefits
&lt;/h1&gt;

&lt;p&gt;After turning the xlsx file into a web application the formulas are fully protected. In addition, the financial professional can still modify his formulas from time to time without changing my Java code since the analysis is implemented in his formulas instead of Java.&lt;/p&gt;

&lt;h1&gt;
  
  
  Complete Source Code
&lt;/h1&gt;

&lt;p&gt;You can check the full source code at &lt;a href="https://github.com/keikai/dev-ref" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>web</category>
      <category>java</category>
      <category>spreadsheet</category>
      <category>excel</category>
    </item>
    <item>
      <title>Automate Excel Sheet Processing with Java</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Tue, 19 May 2020 04:34:57 +0000</pubDate>
      <link>https://dev.to/hawkchen/automate-excel-sheet-processing-with-java-hb4</link>
      <guid>https://dev.to/hawkchen/automate-excel-sheet-processing-with-java-hb4</guid>
      <description>&lt;h1&gt;
  
  
  A Sheet Processing Use Case
&lt;/h1&gt;

&lt;p&gt;My colleague Penny in the HR department has to do a routine task each month: making payroll sheets for everyone in the company. There is a sheet containing a table of all employees with salary information:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnce5xh0ooygy03jgrfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgnce5xh0ooygy03jgrfs.png" alt="Imgur" width="464" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And she has to create payroll sheets for each person on the list based on the template sheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjs49a8uaaibl0xg2ik9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjs49a8uaaibl0xg2ik9.png" alt="Imgur" width="772" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To avoid copying cell by cell manually, I create a web application with &lt;a href="http://keikai.io/" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt; to read salary data row by row and produce payroll sheets. The whole process is:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8m0ii6k4qhkm82sjivy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8m0ii6k4qhkm82sjivy.png" alt="Imgur" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Import a Pre-designed Excel File
&lt;/h1&gt;

&lt;p&gt;I create a Keikai spreadsheet by writing tags and attributes in a zul of &lt;a href="https://www.zkoss.org" rel="noopener noreferrer"&gt;ZK framework&lt;/a&gt; which is a UI framework based on Java EE. By specifying at &lt;code&gt;src&lt;/code&gt; attribute with a file path, Keikai can import my &lt;code&gt;Payroll.xlsx&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;spreadsheet&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/WEB-INF/books/Payroll.xlsx"&lt;/span&gt;
                 &lt;span class="na"&gt;maxVisibleColumns=&lt;/span&gt;&lt;span class="s"&gt;"15"&lt;/span&gt; &lt;span class="na"&gt;maxVisibleRows=&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;
                 &lt;span class="na"&gt;hidecolumnhead=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;  &lt;span class="na"&gt;hiderowhead=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
                 &lt;span class="na"&gt;showToolbar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;showSheetbar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;showFormulabar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
                 &lt;span class="na"&gt;apply=&lt;/span&gt;&lt;span class="s"&gt;"io.keikai.devref.usecase.PayrollComposer"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I visit the zul with a browser, Keikai renders the file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuecu1x6t2x7buwjfaia7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuecu1x6t2x7buwjfaia7.png" alt="Imgur" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ZK framework will parse the zul page above and instantiate a Keikai Java object(&lt;a href="https://keikai.io/javadoc/latest/io/keikai/ui/Spreadsheet.html" rel="noopener noreferrer"&gt;&lt;code&gt;Spreadsheet&lt;/code&gt;&lt;/a&gt;) for us to control. &lt;/p&gt;

&lt;h1&gt;
  
  
  Controller
&lt;/h1&gt;

&lt;p&gt;Then I also create a Java controller, &lt;code&gt;PayrollComposer&lt;/code&gt; to access the Keikai Java object and apply the controller on the page at &lt;code&gt;apply&lt;/code&gt; attribute: &lt;code&gt;apply="io.keikai.devref.usecase.PayrollComposer"&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Named Range
&lt;/h1&gt;

&lt;p&gt;I make a cell like a button on the sheet. When I click the cell, Keikai will start to produce payroll sheets. I give a name, &lt;code&gt;Generate&lt;/code&gt;, to the cell.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8q5goo0j83kdkn9ysw6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8q5goo0j83kdkn9ysw6.png" alt="Imgur" width="254" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, I can create a &lt;a href="https://keikai.io/javadoc/latest/io/keikai/api/Range.html" rel="noopener noreferrer"&gt;&lt;code&gt;Range&lt;/code&gt;&lt;/a&gt; object (&lt;code&gt;generateButton&lt;/code&gt;) with the name:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;generateButton = Ranges.rangeByName(sheet, "Generate");&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the related code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayrollComposer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;SelectorComposer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Wire&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Spreadsheet&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;EMPLOYEE_SHEET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Payroll"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Range&lt;/span&gt; &lt;span class="n"&gt;generateButton&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Sheet&lt;/span&gt; &lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doAfterCompose&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doAfterCompose&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBook&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;EMPLOYEE_SHEET&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;generateButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeByName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Generate"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Listen to Button Clicking
&lt;/h1&gt;

&lt;p&gt;Then I register an event listener for the cell (button) clicking by &lt;a href="https://www.zkoss.org/wiki/ZK%20Developer's%20Reference/Event%20Handling/Event%20Listening" rel="noopener noreferrer"&gt;&lt;code&gt;@Listen&lt;/code&gt;&lt;/a&gt; and check whether the button &lt;code&gt;generateButton&lt;/code&gt; is clicked or not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Listen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ON_CELL_CLICK&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"=spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onCellClick&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CellMouseEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sheetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSheet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSheetName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheetName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;EMPLOYEE_SHEET:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RangeHelper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRangeClicked&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generateButton&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                    &lt;span class="n"&gt;fillPayrollSlips&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the button is clicked, it just starts to fill the payroll slips.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fill payroll data into sheets
&lt;/h1&gt;

&lt;p&gt;Before filling a payroll sheet, I have to read all employees' salary data row by row from the table. The table also has a corresponding named range, &lt;code&gt;PayrollTable&lt;/code&gt;, that I don't need to hard code its cell address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fillPayrollSlips&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PayrollTable"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Range&lt;/span&gt; &lt;span class="n"&gt;payrollRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeByName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sheet&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employeeSalaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getEmployeeSalaries&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payrollRange&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;generateAllPayrollSlips&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employeeSalaries&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid filling data into a fixed cell address of the payroll sheet, I create a named range for each field that maps the corresponding column name. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Column Name&lt;/strong&gt; &amp;lt;==&amp;gt; &lt;strong&gt;Named Range&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3dkdztw65yej8zptckb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff3dkdztw65yej8zptckb.png" alt="Imgur" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hence, I can fill data in a simple loop. Just clone (&lt;a href="https://keikai.io/javadoc/latest/io/keikai/api/Range.html#cloneSheet-java.lang.String-" rel="noopener noreferrer"&gt;cloneSheet()&lt;/a&gt;) the template sheet and fill each field by the named range.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;generateAllPayrollSlips&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employeeSalaries&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;employeeSalaries&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Sheet&lt;/span&gt; &lt;span class="n"&gt;payrollSheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBook&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Form"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cloneSheet&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keySet&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeByName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payrollSheet&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;setCellValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the named range, I don't need to hard code a cell address in the code, which makes the code more robust against sheet layout change.&lt;/p&gt;

&lt;h1&gt;
  
  
  Source Code
&lt;/h1&gt;

&lt;p&gt;I was able to turn this manual Excel copy-pasting task into an automated task in less than 100 lines. I hope you find this example interesting. You can find out the complete code at &lt;a href="https://github.com/keikai/dev-ref/blob/master/src/main/java/io/keikai/devref/usecase/PayrollComposer.java" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web</category>
      <category>spreadsheet</category>
      <category>java</category>
      <category>excel</category>
    </item>
    <item>
      <title>Enable User Input in My Spreadsheet-based App</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Fri, 06 Mar 2020 09:59:07 +0000</pubDate>
      <link>https://dev.to/hawkchen/enable-user-input-in-my-spreadsheet-based-app-2k3b</link>
      <guid>https://dev.to/hawkchen/enable-user-input-in-my-spreadsheet-based-app-2k3b</guid>
      <description>&lt;h1&gt;
  
  
  New requirements: add new expenses
&lt;/h1&gt;

&lt;p&gt;A while ago I &lt;a href="https://dev.to/hawkchen/how-i-created-an-online-budget-report-from-excel-files-g3g"&gt;turned my colleague Michelle's Excel file into an online budget report web app&lt;/a&gt;, to save her from a bunch of manual routines. Recently, she had a new requirement, adding new expenses. She wanted to add new expenses in the sheet and aggregate the new expenses in the current summary report.&lt;/p&gt;

&lt;p&gt;If you are new to my blog, this is how the summary report looks like: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dergnc0ea1gvsmnplgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dergnc0ea1gvsmnplgi.png" width="587" height="575"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Turn cells into a button
&lt;/h1&gt;

&lt;p&gt;We both agreed to keep the existing summary report sheet clean and implement the new feature in a new sheet. We decided to put a button "Add my new expense" on the sheet which will open a new form in another sheet.&lt;/p&gt;

&lt;p&gt;How can we make a button here? It was very simple. Michelle just merged several cells and set her preferred background-color in the Excel template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvhy6rgxx6q05r8zb5fk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvhy6rgxx6q05r8zb5fk.png" width="580" height="656"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Create a form
&lt;/h1&gt;

&lt;p&gt;To fulfill the new requirement, Michelle needed a form to input new expenses. I asked her to open up the Excel template and add a new sheet with a table and a "Done" button. She also used formulas (e.g. &lt;code&gt;=C4*D4&lt;/code&gt;) in the subtotal column (column E).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gbc34ngwhrslnxvb2r6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gbc34ngwhrslnxvb2r6.png" alt="Imgur" width="514" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To avoid accidentally deleting the formulas, she protected the whole sheet and set &lt;code&gt;A4:D7&lt;/code&gt; as "unlocked". Therefore, she can only input new expenses in the unlocked range.&lt;/p&gt;
&lt;h1&gt;
  
  
  Change UI without programming
&lt;/h1&gt;

&lt;p&gt;As you may have noticed, everything Michelle added in the sheets so far can be done in Excel, without any programming skills. All I (as a developer) need to do is to take this updated Excel file and import it into Keikai without asking her what she has changed. Then the updated file becomes our new UI.&lt;/p&gt;
&lt;h1&gt;
  
  
  Sheet switching
&lt;/h1&gt;

&lt;p&gt;After she finished, it was my turn to add the application logic behind these 2 sheets (summary and new expense form). When Michelle clicked "Add my new expense", Keikai should switch to the new expense form sheet.&lt;/p&gt;

&lt;p&gt;Here is the code to add an &lt;code&gt;onCellClick&lt;/code&gt; listener and select the &lt;code&gt;NEW&lt;/code&gt; expense form sheet in the controller class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BudgetComposer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;SelectorComposer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="nd"&gt;@Listen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ON_CELL_CLICK&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"= #spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CellMouseEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Range&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClickedCell&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add my new expense"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCellValue&lt;/span&gt;&lt;span class="o"&gt;())){&lt;/span&gt;
            &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NEW&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Read user input
&lt;/h1&gt;

&lt;p&gt;The final part is to read the new expenses when Michelle clicked the "Done" button.&lt;/p&gt;

&lt;p&gt;Let's update the previous event listener for "Done" button clicked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Listen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ON_CELL_CLICK&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"= #spreadsheet"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CellMouseEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Range&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClickedCell&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add my new expense"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCellValue&lt;/span&gt;&lt;span class="o"&gt;())){&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCellValue&lt;/span&gt;&lt;span class="o"&gt;())){&lt;/span&gt;
            &lt;span class="n"&gt;readExpense&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SUMMARY&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;loadExpenseToSheet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through &lt;code&gt;Range&lt;/code&gt; API, I can read data cell by cell to construct an &lt;code&gt;Expense&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Expense&lt;/span&gt; &lt;span class="nf"&gt;readExpense&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Expense&lt;/span&gt; &lt;span class="n"&gt;expense&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Expense&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCategory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getCellData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getStringValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getCellData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getDoubleValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQuantity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getCellData&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getDoubleValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSubtotal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid boring you to tears, I won't show all the details here. You can take a look at the &lt;a href="https://keikai.io/demo/database" rel="noopener noreferrer"&gt;online demo&lt;/a&gt; to check its source code and understand this application better.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spreadsheet</category>
      <category>excel</category>
    </item>
    <item>
      <title>How I Created an Online Budget Report from Excel Files</title>
      <dc:creator>Hawk Chen</dc:creator>
      <pubDate>Wed, 19 Feb 2020 06:36:39 +0000</pubDate>
      <link>https://dev.to/hawkchen/how-i-created-an-online-budget-report-from-excel-files-g3g</link>
      <guid>https://dev.to/hawkchen/how-i-created-an-online-budget-report-from-excel-files-g3g</guid>
      <description>&lt;h1&gt;
  
  
  Issues when publishing an Excel file
&lt;/h1&gt;

&lt;p&gt;My colleague Michelle in the finance team has a routine job; she has to publish a budget report on a weekly basis. Previously, she had to query budget data from a database. Then, fill the budget data manually into a template sheet like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzbpnajt215ggjbe4hu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzbpnajt215ggjbe4hu0.png" alt="template" width="482" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since this is a routine task, I feel I can help her by automating the data-filing process so that she does not have to do it manually &amp;amp; repeatedly.&lt;/p&gt;

&lt;p&gt;Another issue is that she has been publishing the budget report as an attachment. Readers have to download the file to see the content on an Excel-ready device. Also, it is hard to control where the attachment ends up to, she has to remove all sensitive and raw data before sending it out.&lt;/p&gt;

&lt;p&gt;To solve the above issues, I created a web page with a web spreadsheet component, &lt;a href="https://keikai.io" rel="noopener noreferrer"&gt;Keikai&lt;/a&gt;. Keikai allows me to reuse the existing Excel budget template so that I don’t need to create everything from scratch. It is also important that I can control it with Java.&lt;/p&gt;

&lt;h1&gt;
  
  
  Load the template file first
&lt;/h1&gt;

&lt;p&gt;To start simple, I loaded the Excel template sheet into Keikai to show it in a browser:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo5qvbg2isvex420otxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo5qvbg2isvex420otxw.png" alt="Imgur" width="610" height="858"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Such page was created by &lt;a href="http://books.zkoss.org/wiki/ZUML_Reference" rel="noopener noreferrer"&gt;ZUL&lt;/a&gt;, an XML format language from &lt;a href="http://www.zkoss.org/" rel="noopener noreferrer"&gt;ZK framework&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;spreadsheet&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ss"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/WEB-INF/books/budget.xlsx"&lt;/span&gt;
    &lt;span class="na"&gt;maxVisibleRows=&lt;/span&gt;&lt;span class="s"&gt;"150"&lt;/span&gt; &lt;span class="na"&gt;maxVisibleColumns=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt;
    &lt;span class="na"&gt;showContextMenu=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;showToolbar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;showSheetbar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; 
    &lt;span class="na"&gt;showSheetTabContextMenu=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;showFormulabar=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you can also use Keikai with &lt;a href="https://github.com/keikai/dev-ref/blob/master/src/main/webapp/jsp/index.jsp" rel="noopener noreferrer"&gt;JSP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tag, &lt;code&gt;&amp;lt;spreadsheet&amp;gt;&lt;/code&gt;, represents the Keikai spreadsheet, and I can configure Keikai via tag attributes. &lt;/p&gt;

&lt;p&gt;When a user visits &lt;a href="http://localhost:8080/database.zul" rel="noopener noreferrer"&gt;http://localhost:8080/database.zul&lt;/a&gt;, keikai will respond with a set of javascript and CSS and render the content file in the browser.&lt;/p&gt;

&lt;h1&gt;
  
  
  Data Access Object
&lt;/h1&gt;

&lt;p&gt;In order to load data from the database, I created &lt;code&gt;Expense.java&lt;/code&gt; to store each record and &lt;code&gt;ExpenseDao.java&lt;/code&gt; to query and update the database.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a controller to populate data
&lt;/h1&gt;

&lt;p&gt;Next, I implemented a Java controller class to populate data from the database. The controller has to extend &lt;code&gt;SelectorComposer&lt;/code&gt; so that it can inject &lt;code&gt;Spreadsheet&lt;/code&gt; objects for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BudgetComposer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;SelectorComposer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Wire&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Spreadsheet&lt;/span&gt; &lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@Wire&lt;/code&gt; can inject a &lt;code&gt;Spreadsheet&lt;/code&gt; object on the page, that's why I didn't call any constructor like &lt;code&gt;new Spreadsheet()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Range API
&lt;/h2&gt;

&lt;p&gt;Then, I needed to set data into cells by &lt;code&gt;Range&lt;/code&gt;. One &lt;code&gt;Range&lt;/code&gt; object can represent one or more cells/rows/columns, or even one sheet. I can get a &lt;code&gt;Range&lt;/code&gt; object with a factory method &lt;code&gt;Ranges.range(Sheet targetSheet, int rowIndex, int columnIndex)&lt;/code&gt;. For example, &lt;code&gt;Ranges.range(currentSheet, 0, 0)&lt;/code&gt; represents &lt;code&gt;A1&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To show data in a cell, just call &lt;code&gt;range.setCellValue()&lt;/code&gt;. So the code is quite straightforward. After querying a list of &lt;code&gt;Expense&lt;/code&gt;, I can populate data into cells in a loop like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fillExpenses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Expense&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Expense&lt;/span&gt; &lt;span class="n"&gt;expense&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;START_ROW&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCellValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getQuantity&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Ranges&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spreadsheet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSelectedSheet&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;START_ROW&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCellValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expense&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSubtotal&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Result
&lt;/h1&gt;

&lt;p&gt;I was able to quickly turn my colleague’s manual Excel routine into an automated Web app. Now whenever we access the budget report page, Keikai queries the data and displays &lt;br&gt;
it within the report template like:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dergnc0ea1gvsmnplgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dergnc0ea1gvsmnplgi.png" width="587" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Try it yourself
&lt;/h1&gt;

&lt;p&gt;If you find this article interesting, there are more examples and &lt;a href="https://keikai.io/demo/database" rel="noopener noreferrer"&gt;demos&lt;/a&gt; on &lt;a href="https://keikai.io" rel="noopener noreferrer"&gt;Keikai’s website&lt;/a&gt;. Or you can run the example project on &lt;a href="https://github.com/keikai/dev-ref" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>spreadsheet</category>
      <category>web</category>
      <category>excel</category>
    </item>
  </channel>
</rss>
