APEX 5.2 Clipboard

Note this is about a future release of APEX and the final release may differ from what I describe.

This is the second article in my series about APEX 5.2 [Update: the release is renamed 18.1]. Yesterday’s topic was Interactive Grid and today’s is clipboard.

Copy to clipboard started out specific to Interactive Grid (IG) but grew into something more general. First the reason why IG needs copy to clipboard when Interactive Report could just use standard web page text selection is that UI controls that support selection generally don’t support text selection. For example, this is true for the native HTML select element. The mouse and keyboard interactions that would select text are used for other things. When a UI control has its own concept of selection, be it rows, cells, items or nodes it makes sense to copy the selected thing to the clipboard. The grid widget in IG is not the only APEX widget that supports selection. The others are treeView (used by the Tree region) and iconList (used in IG icon view, IG dialogs, and various places in the builder). Clipboard support was added to these as well. (The page designer grid layout widget also supports selection but clipboard support was not added there; maybe someday.)

Any easy was to see this feature at work in your APEX 5.2 EA1 workspace is: (If you don’t have one go request a workspace now.)

  • Open any page in page designer
  • Select a page item or region column in the rendering tree (works with other nodes as well)
  • Press Ctrl+C (Command+C on a Mac)
  • Then edit an attribute such as SQL or PL/SQL where you want to use the item or column name as a bind variable or substitution.
  • Press Ctrl+V (Command+V on a Mac) to paste.

This even works if you select multiple tree nodes. The labels of the nodes are copied. When you paste you will still need to edit the syntax to turn, for example, P1_ITEM into &P1_ITEM. or :P1_ITEM. Some people have proposed the copy and paste to be more context sensitive so that, for example, multiple item names would be separated by commas when pasting into a Page Items to Submit attribute. In general this would require more information about what is in the clipboard and more about the context of the paste especially within SQL or PL/SQL code. So far this is not a page designer specific feature. It is just a useful side effect now that all APEX treeView widgets including Tree regions support copy to clipboard.

The JavaScript clipboard API is widely supported among modern browsers but also has many inconsistencies and oddities around when and how it can be used. Browsers are picky about when the clipboard APIs can be called, where focus must be at the time, and if there is a text selection. In some cases it may be for security reasons. Web pages should not be able to spy on the OS clipboard. To support copy to clipboard for multiple widgets much of the complexity to work across browsers was added to a new namespace apex.clipboard (in clipboard.js). This is currently not documented but may be in the future.

If you maintain or create an APEX item or region plug-in that supports selection you may want to use apex.clipboard to copy the selection to the clipboard. This is not trivial but definitely easier than using the clipboard API directly. See how the grid widget (widget.grid.js) calls apex.clipboard.addHandler.

An extra bonus feature is declarative support to copy the contents of a page item to the clipboard at the click of a button. The use case is that the app produces some output that the user may want to copy and paste to another application. The output could be a URL, markup, some code, an email template, etc. What is typically done is to display that output in a display only item or a read-only text field. The user then needs to select the text and copy it (this can be done with either the mouse or keyboard but the user needs to know how to do it and it is multiple steps). Now it is easy to add a copy to clipboard button.

To add a copy to clipboard button do the following:

  • Create a text area or text field item called P1_TO_COPY for example.
  • Set the source value to something you want the user to put on the clipboard.
  • Make it readonly by setting Advanced: Custom Attributes = readonly. Using the Readonly: Type = always attribute will also work but you loose the ability to focus the text input or textarea.
  • Create a button with label “Copy to Clipboard”. An icon button with icon fa-clipboard works nicely.
  • Set the button Behavior: Action to Defined by Dynamic Action even though it isn’t.
  • In the button’s Advanced: Custom Attributes enter data-clipboard-source=”#P1_TO_COPY”. This is the key step.

Test it by running the page and clicking the button. Then go to some other app where you can paste text and see that the item value was copied. There are open source libraries available to do this kind of thing and if you can’t wait for 5.2 go ahead and use them. Once 5.2 is available it makes sense to use the functionality that is built in.

You can see that we put this feature to good use in the new ORDS RESTful Services to copy the full URL of the module definition. Go to SQL Workshop > RESTful Services and choose ORDS then create a module if you haven’t already. You will see a copy button after the Full URL.

Copy to Clipboard

Clipboards in general and the JavaScript clipboard API support multiple formats for the clipboard data. I found that browsers limit the formats that can be used in practice. Most browsers just ignore any attempt to set unsupported formats. IE11 and Edge give a JavaScript error for anything but text. My initial thought was that text/csv would be an ideal format for grid data but I found that even programs like Excel and LibreOffice Calc would not take it. It turns out that simple HTML table markup is preferred by word processors and spreadsheet programs. For plain text editors tab delimited columns with CRLF delimited rows works best. The treeView and iconList support text/plain with each node/item label on its own line and text/html using list markup. The apex.clipboard code makes sure that IE and Edge safely fallback to text only while other browsers can use plain text and html.

Using the Sample Interactive Grids app on your EA1 workspace try the copy to clipboard feature. There is a bug copying whole rows in an editable grid so example page Reporting: Multiple Selection is a good one to test with. Try pasting into a spreadsheet, text editor, or word processor. You can copy whole rows or a range of cells. Press F8 or use the menu Actions > Selection > Cell Selection to switch to cell range selection mode. Use the Shift key with mouse or arrow keys to select a range. Change back to row selection mode with F8 or menu Actions > Selection > Row Selection.

I can imagine some people will have special use cases for copying IG data. For example some may want to include the row headers and some may want to copy a select list column value rather than display value. I’m sure there are more use cases that I have not even thought of. IG can’t handle all of these competing use cases so it copies what most closely represents what is selected. Internally a format writer interface is used that gives a great deal of control over what gets put on the clipboard. The plan is not to document this, at least not yet. If you are interested or have a strong need to customize the clipboard data take a look at the widget.grid.js code dataTransferFormats option.

There are a few ways to copy the selection:

  • Ctrl+C while focus is in the widget.
  • Browser context menu (not supported by Chrome).
  • Application or region supplied button or menu.

The first way, Ctrl+C, should have the widest range of browser support. The browser context menu support depends on the browser. Some show Cut, Copy, and Paste but only Copy is enabled. Some just show Copy and others have no clipboard items at all (presumably because focus is not in a native editable element such as a text area). The Select All item on the browser context menu is not useful but I don’t know of a way to remove it.

Interactive Grid has its own action and menu item to copy to clipboard. You can find it on the Actions > Selection sub menu and on the Selection Actions menu. The Tree region doesn’t have anything like this. You can add a copy to clipboard button or menu item with this code:

    $("#myTree").treeView( "focus" );
    apex.clipboard.copy();

This is typical for any widget that uses apex.clipboard. First you put focus in the widget and then call the copy method. For IG you would simply invoke the selection-copy action.

It is natural to ask What about cut and paste? I did look into supporting these but found it would be impractical to do so in a seamless and reliable cross browser way. In addition there are questions about desired behavior for cut and paste with IG. The normal behavior of cut is to delete the selection but IG normally just marks rows for deletion. A spreadsheet simply clears cut cells but IG is not a spreadsheet. The question for paste is should it overwrite the cells like a spreadsheet or insert new rows? Does the answer depend on if you are in row or cell selection mode? IG has columns that hold specific data so you would have to be careful that the pasted columns line up. What happens if there are fewer (or more) columns pasted than the IG has? It starts to get into the issues that a CSV data upload wizard handles. Thankfully due to the technical issues we never had to deal with these questions of IG cut and paste behavior.

Again, try out the 5.2 Early Adopter and let us know what you think.

Keep an eye out for the next topic in this series which will be the jQuery and JET library upgrade.