CHAPTER 10

image

Plugins

by Dan McGhan

APEX has long been extensible. It was built in such a way that developers could add custom content to just about any part of a page constructed by the framework. The problem, however, was twofold. First, you had to be familiar enough with the related technologies to work outside the declarative environment that APEX provides. Second, even if you had enough knowledge to do a customization, repeating it on another page or in another application altogether was often quite cumbersome.

The APEX plugin architecture, introduced with APEX 4.0, solves the latter of those problems. You still need to have sufficient knowledge of the related technologies, which most often include SQL, PL/SQL, HTML, CSS, and JavaScript. But, provided you possess this knowledge, you can integrate customizations to APEX in a self-contained, easily reusable, and sharable way—as a plugin.

The plugin architecture has evolved quite a bit since APEX 4.0, with enhancements having been added in versions 4.1, 4.2, and most recently 5.0. There are currently six types of plugins, all of which extend native APEX component types, including regions, items, processes, dynamic actions, authentication schemes, and authorization schemes.

There are two main audiences for plugins: those who will build plugins and those who will use what others build. This chapter is written for the former—for APEX developers interested in learning how to create plugins for themselves and others to use. I will not cover plugin installation or configuration because I assume you already possess this knowledge.

The target audience for this chapter is one that is comfortable working with SQL and PL/SQL and has at least some working knowledge of HTML, CSS, and JavaScript. If you feel this doesn’t describe you perfectly, don’t be too worried. The tutorial in this chapter was written in such a way that you should be able to follow along, even if you don’t quite understand everything you’re doing.

Developing a plugin is a unique and rewarding experience. You may find yourself challenged at times as you constantly move between client-side and server-side programming languages while developing a reusable piece of an APEX application. But if you stick to it, eventually your efforts will pay off and you (and ideally the entire APEX community) will be able to enjoy the fruits of your labor.

The APEX Plugin Architecture

The pieces of the APEX plugin architecture that plugin developers work with include pages in the shared components of an application and PL/SQL APIs. The pages in the shared components are used to create and modify plugins, while the PL/SQL APIs provide required constructs as well as utility functions that minimize the amount of “plumbing” you might otherwise need to code. This section of the chapter will provide an introduction to these new components.

Create/Edit Page for Plugins

The Create/Edit plugin page is the main page used while developing plugins, so it’s important to become acquainted with it. To navigate to the page, first go to the Plug-ins page by clicking the Plug-ins option in the Shared Components area (under Other Components). Once there, you can either drill down on an existing plugin or click the Create button and complete the Create Plug-in Wizard to create a new plugin.

The Create/Edit plugin page contains 13 regions in total. You may notice subtle differences when viewing this page at different times. Your mind is not playing tricks on you; the page changes depending on a number of factors. For example, not all regions are available at all times or for all plugin types. Also, the items in a region, as well as the times at which those items can be edited, can vary. All of this will be explained in the sections that follow.

Name

The Name region (see Figure 10-1) contains items that allow plugin developers to identify a plugin as well as the plugin type.

9781484204856_Fig10-01.jpg

Figure 10-1. The Name region of the Create/Edit page

You can think of the Name attribute as the display name for plugin users. Plugin users will see and select this name to use the plugin via the declarative interface provided by the Application Builder. Exactly where they see the name will depend on the plugin type; for example, if you create an item plugin, the Name value will be displayed in the Type attribute of items in the application (see Figure 10-2).

9781484204856_Fig10-02.jpg

Figure 10-2. An item plugin’s name displayed in the Type attribute of a page item

The Internal Name attribute serves as a unique identifier for the plugin within an application. The Internal Name attribute should be uppercase; lower- or mixed-case values will be converted to uppercase when creating or updating the plugin. Note that this attribute cannot be modified if the plugin is being used in the application.

When importing a plugin into an application, the value of the Internal Name attribute will be checked against other plugins in the application. If a plugin with the same Internal Name attribute already exists, users will need to confirm that they want to replace the existing plugin. For this reason, uniqueness of the Internal Name attribute is much more important than uniqueness of the Name attribute.

Image Note  See the “Plugin Best Practices” section later in this chapter for tips on naming and other topics.

The Type attribute, as I’m sure you’ve already guessed, defines the type of plugin you are working with. The Type attribute is obviously a major attribute of a plugin, and as such its value drives some of the other regions and items that are visible on the page. This attribute, like the Internal Name attribute, cannot be modified if the plugin is being used in the application.

The Category attribute (not visible in Figure 10-2) is available only for dynamic action plugins. This attribute is used to group similar dynamic actions together, which makes selection a little easier for plugin users; it has no other impact.

Subscription

The Subscription region (see Figure 10-3) is the same as it is throughout APEX. The Reference Master Plug-in From item allows you to link to a “master” copy of the plug-in. The master copy often resides in a centralized application used only for this purpose. Once established, the link between master and child allows for updates to be performed on a one-off basis from the child or in bulk from the master to all children.

9781484204856_Fig10-03.jpg

Figure 10-3. The Subscription region of the Create/Edit page

Source

Plugin developers must write one or more PL/SQL functions for their plugin to work. The PL/SQL Code attribute in a region (see Figure 10-4) is where the code for these functions typically resides; however, it is possible to use code compiled in the database instead. The number and type of functions required vary depending on the plugin type—see the “Callbacks” section for more details.

9781484204856_Fig10-04.jpg

Figure 10-4. The Source region of the Create/Edit page

Callbacks

The Callbacks region (see Figure 10-5) is used to register the PL/SQL functions you have defined (see the earlier “Source” section) with what is expected by the framework for the given plugin type. Simply add the function name, and the framework will invoke the function at the appropriate time (except the AJAX function, which is manually invoked when needed via JavaScript).

9781484204856_Fig10-05.jpg

Figure 10-5. The Callbacks region of the Create/Edit page

You can use any valid Oracle name for the functions, but the signature must match that which is expected for a given function type. Exactly which functions are available, required, or optional will vary based the plugin type. The easiest way to look up the expected signature is to click the help button (question mark on the right) for the function name. If the function is compiled in a package, simply prefix the function name with the package name followed by a period.

The number and types of functions available will vary depending on the plugin type. The most common functions include the render function, which is used to render the plugin on the page; the AJAX function, which is used to make asynchronous calls from the plugin to the server for additional data and processing; and the execution function (not displayed), which is used to perform some type of processing.

User Interfaces

The “Supported for” attribute of the User Interfaces region (see Figure 10-6) is used to specify which user interfaces your plugin will work with. Keep in mind that desktop and mobile UIs can be quite different. APEX uses jQuery Mobile for its mobile UI, so plugin developers who want to add support for that UI may need to learn about how jQuery Mobile expects its markup, what events are available and how they work, and so on.

9781484204856_Fig10-06.jpg

Figure 10-6. The “Supported for” attribute of the User Interfaces region

Standard Attributes

Plugin developers can use two main types of attributes in plugins: standard (covered here) and custom (covered in the next section). Both standard and custom attributes are a plugin developer’s means to provide declarative options to plugin users who can change the characteristics or behavior of a plugin.

In APEX, many attributes for a given component type are the same. For example, the Select List and Check Box items both have a List of Values definition. Likewise, both the Interactive and Classic Report regions have a Source attribute that requires a SQL query. Standard attributes give plugin developers the ability to add these types of common attributes to their plugins. Enabling standard attributes in plugins will simply present those options to plugin users when creating a new instance of the plugin. The values entered or selected by the developer will be passed to the plugin logic via parameters in the callback functions. It is the responsibility of plugin developers to check the values of these attributes and code the plugin to respond appropriately.

Always try to use standard attributes before creating custom equivalents. This will save you time and reduce the number of custom attributes used by your plugins. More importantly, standard attributes will be displayed where APEX developers are already familiar with them and in a way that’s consistent with other instances of a particular component type.

The standard attributes available vary by plugin type. Figures 10-7 through 10-11 show the standard attributes available to item, region, dynamic action, process, and authentication scheme plugins. Authorization scheme plugins do not have any standard attributes.

9781484204856_Fig10-07.jpg

Figure 10-7. The Standard Attributes region of the Create/Edit page for the Modal LOV plugin (an item plugin)

9781484204856_Fig10-08.jpg

Figure 10-8. The Standard Attributes region of the Create/Edit page (a region plugin)

9781484204856_Fig10-09.jpg

Figure 10-9. The Standard Attributes region of the Create/Edit page (a dynamic action plugin)

9781484204856_Fig10-10.jpg

Figure 10-10. The Standard Attributes region of the Create/Edit page (a process plugin)

9781484204856_Fig10-11.jpg

Figure 10-11. The Standard Attributes region of the Create/Edit page (an authentication scheme plugin)

Custom Attributes

The standard attributes, while extremely useful, could never include all the attributes you might want to add to a plugin. To remedy this fact, the APEX plugin architecture allows you to create custom attributes (see Figure 10-12) that are exposed to plugin users as settings.

9781484204856_Fig10-12.jpg

Figure 10-12. The Custom Attributes region of the Create/Edit page for the Modal LOV plugin

To add an attribute to a plugin, simply click the Add Attribute button. This will redirect you to the Edit Attribute page where the attribute can be configured. When creating a new custom attribute, you will need to specify the type. There are 23 different types of custom attributes that can be added to a plugin (see Figure 10-13).

9781484204856_Fig10-13.jpg

Figure 10-13. The Type options available when creating a custom attribute

The type is used to determine how the attribute should be displayed to plugin users and in some cases can allow the plugin framework to ensure that valid values have been entered. Custom attributes can be made to display conditionally (based on the value of other custom attributes). You can create up to 25 custom attributes at the application level and another 25 custom attributes at the component level.

Placing attributes at the application level of a plugin is a good way to reduce the number of decisions that plugin users face each time they use a plugin, and it’s a good way to enforce consistency. For example, an attribute that changes the color scheme of a plugin may be best at the application level so that the plugin is displayed the same way on different pages. Plugin users will set application-level attributes via Component Settings in the Shared Components page.

On the other hand, not all attributes are suited for the application level. An attribute used to identify a page-level item may be better off as a component-level attribute. This would allow the plugin to be used more than once in an application without different instances of the plugin interfering with each other. Plugin users will set component-level settings from a Settings region, displayed with the standard attributes, where the component is usually configured.

The Custom Attributes region of the Create/Edit plugin page is available only after the plugin is created. Also, certain parts of custom attributes, such as the scope, attribute (number), and LOV return values, cannot be modified if the plugin is being used in the application.

Files

Plugins often require the use of external files for various functionality. The most common types of files used in plugins include CSS, JavaScript, and image files (see Figure 10-14). Files uploaded here are stored in the database and directly associated with the plugin. It is possible to upload entire ZIP files that include directories. Directory structures will be preserved, so it’s safe to use relative paths within files. Files uploaded to a plugin are not automatically added to the APEX page at runtime—see the “File URLs to Load” section to see how to accomplish that task.

9781484204856_Fig10-14.jpg

Figure 10-14. The Files region of the Create/Edit page

File URLs to Load

The File URLs to Load region (see Figure 10-15) allows plugin developers to declaratively add files to the APEX page that the plugin depends on. The #PLUGIN_FILES# substitution string can be used to point to files that have been uploaded to the plugin via the Files region. The #MIN# and #MIN_DIRECTORY# substitution strings will be replaced with .min and minified/, respectively, when you are not logged into APEX as a developer (typically test and production). It is up to the plugin developer to create the minified/compressed versions of their CSS and JavaScript if they want to use these substitution strings.

9781484204856_Fig10-15.jpg

Figure 10-15. The File URLs to Load region of the Create/Edit page

CSS files will be added to the APEX page where the #APEX_CSS# substitution string is included in the page template header (the header of the HTML document). JavaScript files, on the other hand, will be added to the APEX page where the #GENERATED_JAVASCRIPT# substitution string in the page template footer (the bottom of the body of the HTML document). These locations follow best practices for adding CSS and JavaScript to web pages.

Events

The Events region (see Figure 10-16) allows plugin developers to add custom events to dynamic actions in an application. This ability would be useful only for plugins that trigger custom events. For example, if you created a countdown timer plugin, creating a “timeout” event would allow plugin users to create dynamic actions that react to the event. It’s up to plugin developers to trigger custom events at the appropriate times. The Events region is visible only after the plugin is created.

9781484204856_Fig10-16.jpg

Figure 10-16. The Events region of the Create/Edit page

Information

The Information region (see Figure 10-17) allows you to add the version number of the plugin as well as specify an About URL. The About URL should point to a location on the Web where plugin users can learn more about the plugin.

9781484204856_Fig10-17.jpg

Figure 10-17. The Information region of the Create/Edit page

Help Text

The Help Text region (see Figure 10-18) allows plugin developers to add built-in documentation for plugin users. Any documentation added by plugin developers will be visible to plugin users via the same region.

9781484204856_Fig10-18.jpg

Figure 10-18. The Help Text region of the Create/Edit page

Comments

The Comments region (see Figure 10-19) is similar to how it is with other components throughout APEX. The main difference here is that there are two types of users who may use the comments: plugin developers and plugin users (APEX developers). Plugin developers need to be aware that comments added to a plugin are exported with the plugin and are visible when it’s later imported.

9781484204856_Fig10-19.jpg

Figure 10-19. The Comments region of the Create/Edit page

PL/SQL APIs

Many tasks involved with developing plugins are somewhat common. Here are some examples:

  • Reading plugin settings and altering the behavior of the plugin
  • Querying data and processing the results
  • Performing AJAX calls

A number of PL/SQL APIs have been developed to help make these tasks easier to complete than they would otherwise be. Learning about these APIs is important, even if only at the most basic level, because it could save you from reinventing the wheel when you really only want to build a plugin. This section provides an introduction to the PL/SQL APIs most commonly used for plugin development in APEX.

APEX_PLUGIN

The APEX_PLUGIN package contains a number of core types and functions that are used specifically for plugins. The record types are used in the callback functions of a plugin and provide context and setting values. Here’s an example of a region render function’s signature:

function some_item_render (
   p_item                in apex_plugin.t_page_item,
   p_plugin              in apex_plugin.t_plugin,
   p_value               in varchar2,
   p_is_readonly         in boolean,
   p_is_printer_friendly in boolean
)

   return apex_plugin.t_page_item_render_result

The T_PLUGIN record type, shown previously as the data type for the formal parameter p_plugin, is included as a parameter in every callback function. It provides developers with access to the application-level attributes of a plugin such as the name, file prefix, and any custom attributes added at that level.

Each type of plugin also has a number of dedicated record types defined in the APEX_PLUGIN package that are used in the callback functions: one used as a formal parameter and a number of others used as result types. The record type used as a formal parameter, T_PAGE_ITEM in this example, provides developers with access to the component-level attributes such as the name, standard attributes, and any custom attributes added at that level.

There is one result type defined for each of the callback functions a given plugin type can use. The example shows part of the render function of an item plugin, which uses the corresponding T_PAGE_ITEM_RENDER_RESULT record type as the result type. These result types allow developers to pass information back to the plugin framework such as whether a plugin is navigable.

In addition to the record types, there are two functions defined in the APEX_PLUGIN package as well: GET_INPUT_NAME_FOR_PAGE_ITEM and GET_AJAX_IDENTIFIER. The GET_INPUT_NAME_FOR_PAGE_ITEM function is used only for item plugins. It returns the value plugin developers should assign to the name attribute of the element that contains the correct value for the plugin. APEX uses the name attribute of elements to map an element’s value on the page to the corresponding item’s session state when a page is submitted. Here is an example from an item render function that demonstrates its use:

sys.htp.p(
   ’<input type="password" name="’ ||
   apex_plugin.get_input_name_for_page_item(p_is_multi_value => false) ||
   ’" id="’ || p_item.name ||
   ’" size="’ || p_item.element_width ||
   ’" maxlength="’ || p_item.element_max_length ||
   ’" ’ || p_item.element_attributes || ’/>’);

If you plan to take advantage of AJAX technology in your plugin, you’ll need a means to call the AJAX callback function from JavaScript. To get around various issues related to calling the function by name, the plugin architecture requires you to call the function via a unique identifier, which is obtained from the GET_AJAX_IDENTIFIER function.

The function will work only in the context of the render function and only if an AJAX callback function has been defined. The result of the function call needs to be mapped through to the plugin’s JavaScript code so that it can be used when needed. Here is an example from a region render function that demonstrates how the function is used (see the tutorial section for a complete example):

 l_onload_code :=
   ’apex.jQuery("div#’ || p_region.static_id || ’").calendar({’
    || apex_javascript.add_attribute(
           p_name => ’ajaxIdentifier’,
           p_value => apex_plugin.get_ajax_identifier(),
           p_omit_null => FALSE,
           p_add_comma => FALSE)
    || ’});’;

 apex_javascript.add_onload_code (
    p_code => l_onload_code
 );

APEX_PLUGIN_UTIL

APEX_PLUGIN_UTIL is a utility package that eases the burden of developing plugins by providing production-ready code for many common plugin-related tasks. The package has many functions and procedures related to debugging, SQL and PL/SQL processing, and more.

Depending on their scope and complexity, debugging plugins can be a difficult task. Thankfully, the APEX_PLUGIN_UTIL package comes with a number of debug procedures—at least one for each plugin type. The debug procedures write data about the plugin to the APEX debug log when it is enabled. The name of the plugin and its attribute values are included with the output. Here’s an example from a region render function that demonstrates how the procedure is used:

if apex_application.g_debug
then
  apex_plugin_util.debug_region (
     p_plugin => p_plugin,
     p_region => p_region
  );
end if;

In addition to this basic level of logging, it can be useful to instrument your code using the APEX_DEBUG package. With this package it’s possible to separate logging messages according to levels such as error, warn, and trace, which minimizes the impact of the instrumentation when not needed.

APEX_CSS and APEX_JAVASCRIPT

The APEX_CSS and APEX_JAVASCRIPT packages consist of functions and procedures that allow you to add CSS and JavaScript to your HTML output. Prior to APEX 5.0, these packages were the best options for adding CSS and JavaScript needed in plugins. However, the declarative File URLs to Load region (see “File URLs to Load” earlier) is much easier to use and will satisfy the majority of use cases. Use these packages only when you require more programmatic control over whether or how CSS and JavaScript are added to the page.

APEX_WEB_SERVICE

The APEX_WEB_SERVICE package provides procedures and functions—too many to list here—that allow you to interact with both SOAP- and REST-based web services from PL/SQL. Prior to the introduction of this package, developers could have consumed web services from PL/SQL using other packages such as UTL_HTTP. However, the APEX_WEB_SERVICE package has greatly simplified and standardized how this type of code is written.

APEX_JSON

The APEX_JSON package provides procedures and functions related to creating, parsing, and stringifying JSON. APEX_JSON is a welcome package because JSON has quickly become the standard format by which data is transferred between client and server via AJAX. This package complements the JSON capabilities added to the SQL engine of Oracle Database 12.1.0.2, which allow users to query but not generate JSON data.

Other Packages

In addition to the packages covered in this chapter, there are a number of others that are frequently used while developing plugins in APEX. The Oracle Database PL/SQL Packages and Types Reference document now includes almost 300 packages. At a minimum, I recommend you explore the other APIs outlined in the Oracle Application Express API Reference as well as the packages that comprise the PL/SQL Web toolkit. A small amount of time spent here learning from these documents could save many hours of needless development.

Other Tools of the Trade

As you have seen, the APEX plugin architecture provides a wealth of useful functionality that allows you to create plugins. However, there are some other “tools of the trade” you may find useful. These tools can help you produce well-structured JavaScript code and attractive user interfaces.

jQuery and jQuery UI

jQuery is a JavaScript DOM (think “web page”) manipulation library. jQuery provides simple methods for selecting elements, traversing and manipulating the DOM, executing AJAX calls, and much more. jQuery was created nearly ten years ago, when the DOM API was tedious and inconsistently implemented by various browser manufacturers. Things are much better today, but jQuery continues to be quite popular, especially when support for older browsers is important.

jQuery UI is a collection of lower-level user interface widgets. Some of the widgets included with the library include a date picker, dialog, and slider. jQuery UI can help plugin developers focus on the functionality of the plugins they want to build rather than the lower-level widgets needed to build them. Although jQuery UI includes its own set of icons, it may be worth exploring Font Awesome icons as an alternative because they can be styled better by the Theme Roller that customizes the Universal theme.

APEX includes the “base” jQuery UI theme, which is a neutral-colored theme. Many of the newer APEX themes include CSS that modifies the base theme to give the jQuery UI widgets a more integrated look. If your plugin has a visual component to it, you’ll need to take this into account because the plugin should look as though it’s a native part of the application. Using jQuery UI widgets and classes should go a long way to ensure your plugin looks great with any theme.

A quick Google search will reveal many different sources for learning more about jQuery and jQuery UI, many of which are free. The time spent learning these technologies should easily pay you back time and time again when you develop plugins in APEX.

jQuery UI Widget Factory

If you’re creating a region, item, or dynamic action plugin, then JavaScript will likely be an important part of your plugin. If you come from an Oracle background, you may be comfortable working with PL/SQL but a little apprehensive about JavaScript. Newcomers to JavaScript can spend a lot of time just figuring out a good way to organize their code. The jQuery UI Widget Factory provides just that: a systematic approach to writing JavaScript that provides organization as well as a number of other features.

Because this chapter is about writing plugins for APEX, I will use the term widget in place of plugin when referring to the jQuery UI Widget Factory—although the terms are typically interchangeable. The jQuery UI Widget Factory allows you to create stateful jQuery widgets with minimal effort. Stateful means that the widget keeps track of its attribute values and even allows attribute updates and method calls after the widget has been initialized. One of the best ways to learn about the jQuery UI Widget Factory is to see an example of how it is used. The following tutorial will walk you through the steps needed to build a widget.

This tutorial will create a widget named lengthLimit. The lengthLimit widget will be used to enhance standard input elements so that they warn users when the number of characters entered approaches a defined maximum length. The widget will set the text color to a warning color when the text reaches 80 percent of the maximum length and then to an error color when the limit is reached. The widget will also trim any characters entered in the input that exceed the maximum length.

Image Note  This is intended as a “read-along” tutorial. I’ll use the concepts covered here in the “code-along” tutorial in the “Building a Region Plugin” section of this chapter.

The $.widget function is called to create the widget. The first parameter passed to the function consists of the namespace and name of the widget, which are separated by a period. The ui namespace is used by all the widgets in the jQuery UI library. The second parameter (currently just an empty object) is the prototype to be associated with the widget.

  apex.jQuery.widget("ui.lengthLimit", {
   //widget code here
});

Before continuing to build the widget, it is important to take care of one important issue: $. Most people who use jQuery are familiar with using the dollar sign as a shorthand reference to the jQuery object rather than spelling out jQuery, or as it’s been exposed in APEX, apex.jQuery. While it’s possible to use the dollar sign in your widget code, it’s best to protect references to the dollar sign from collisions with other JavaScript libraries. The technique used to do this is to wrap your code in an immediately invoked function expression, or IIFE (pronounced “ify”), that passes the jQuery object as an actual parameter to itself as a formal parameter, which uses the dollar sign for its name. If this concept sounds complex and confusing, don’t worry—it requires only two lines of code (the first and last in the following example). Note that the previous reference to apex.jQuery has been replaced by the dollar sign now that it is safe to do so.

(function($){
$.widget("ui.lengthLimit", {
   //widget code here
});
})(apex.jQuery);

Let’s continue building the widget by adding the options object. The options object is used to set default values for configuration options used by the widget.

(function($){
$.widget("ui.lengthLimit", {
   options: {
      warningColor: "yellow",
      errorColor: "red",
      maxLength: 50
   }
});
})(apex.jQuery);

Next you’ll add a function named create that will be invoked automatically when the widget is first instantiated.

(function($){
$.widget("ui.lengthLimit", {
   options: {
      warningColor: "orange",
      errorColor: "red",
      maxLength: 50
   },
   _create: function() {
      var uiw = this;

      uiw.element.change(function() {
         var $textElmt = $(this);
         var currLength = $textElmt.val().length;
         var currPercent = currLength/uiw.options.maxLength;

         if (currPercent >= .9) {
            $textElmt
               .val($textElmt.val().substr(0, uiw.options.maxLength))
               .css(’color’, uiw.options.errorColor);
         } else if (currPercent >= .8) {
            $textElmt.css(’color’, uiw.options.warningColor);
         } else {
            $textElmt.css(’color’, ’black’);
         }
      });
   }
});
})(apex.jQuery);

At this point you have a fully functioning widget. You could add this code to the Function and Global Variable Declaration section of an APEX page and then instantiate the widget on a page item by adding the following code to the Execute when Page Loads section. Notice that default option values can be overridden during instantiation.

$(’#P1_FIRST_NAME’).lengthLimit({
   maxLength: 10
});

After a little testing, you may notice two issues. First, the main logic, which sets the color and trims the text, is not executed when the page loads and the widget is initialized. Also, that same logic is wrapped up in an event handler that was bound to the change event of the element. As a consequence, developers have no way to execute that code without manually triggering the change event on the item, which may be undesirable. With a little refactoring, you can solve both issues while keeping the code maintainable.

(function($){
$.widget("ui.lengthLimit", {
   options: {
      warningColor: "orange",
      errorColor: "red",
      maxLength: 50
   },
    _create: function() {
      var uiw = this;

      uiw.element.change(function() {
         uiw.checkLength();
      });

      uiw.checkLength();
   },
   checkLength: function() {
      var uiw = this;
      var $textElmt = uiw.element;
      var currLength = $textElmt.val().length;
      var currPercent = currLength/uiw.options.maxLength;

      if (currPercent >= .9) {
         $textElmt
            .val($textElmt.val().substr(0, uiw.options.maxLength))
            .css(’color’, uiw.options.errorColor);
      } else if (currPercent >= .8) {
         $textElmt.css(’color’, uiw.options.warningColor);
      } else {
         $textElmt.css(’color’, ’black’);
      }
   }
});
})(apex.jQuery);

The majority of the logic has been moved to a new function named checkLength. This function is used twice in the _create function: once in the change event handler on the element and then later at the end of the function.

You may have noticed that the new checkLength function didn’t have the same leading underscore that the _create function did. This is an important distinction. The underscore is used to create “private” functions within a widget. They can help modularize your code but cannot be invoked from outside the widget (at least not as easily as public functions). Because the checkLength function name doesn’t start with an underscore, it’s registered as a public function and can be called at any time as follows:

$(’#P1_FIRST_NAME’).lengthLimit(’checkLength’);

Also note the subtle yet important change in reference to this. With the majority of code in a widget, this will refer to the widget instance. But in the original _create function, an anonymous function was bound to the change event of the element. When that function is executed, this will refer to the element that changed. After the refactoring, the element property of the widget instance (available automatically as part of the factory) was needed to access the same element—the element on which the widget was instantiated. Additionally, a local variable is declared in each function to hold a reference to this. This technique is optional but can help to avoid confusion and to take advantage of closure in JavaScript.

This introduction to the jQuery UI Widget Factory is by no means complete, but you should now have a basic understanding of how it can be utilized to write well-structured JavaScript with minimal plumbing. To learn more about the Widget Factory, find the “official” documentation available on the jQuery and jQuery UI web sites as well as the many tutorials found elsewhere on the Web.

Font Awesome

Font Awesome is an iconic font and CSS toolkit that is included with APEX. The latest version of Font Awesome as of this writing, 4.3, includes 519 icons! The icons were designed to scale from very small to very large without losing clarity or becoming distorted. While jQuery UI includes its own icons, they are image based and cannot be styled as easily as the font-based icons included with Font Awesome.

Font Awesome is easy to use; just create an icon tag with the right classes. Here’s a simple example of the markup needed to create a font awesome icon: <i class="fa fa-smile-o"></i>. When rendered in APEX, users will see a happy face. Want to make it twice as big? Just add the fa-2x class. Want to spin it in circles? Just add the fa-spin class. You get the idea: it’s easy to use Font Awesome!

To learn more about Font Awesome icons and view the library, visit http://fortawesome.github.io/Font-Awesome. Keep in mind that while developers are able to update the version of Font Awesome that is being used in their APEX applications, you should not make any assumptions about this. Only use icons that are available in the version of Font Awesome that is included by default with the version APEX for which you wrote the plugin.

Building a Region Plugin

In the first two sections of this chapter, you explored the APEX plugin architecture and other tools of the trade that can help when building plugins. Now you’ll put all you’ve learned to use as you build a region plugin that allows developers to show data in a grid with some columns frozen on the left. The APEX plugin will really be a wrapper for the jqGrid plugin, which itself is a jQuery UI Widget; it’s the plugin version of Inception.

Image Note  You can download the code listings in this section of the chapter from Apress.

The Base Plugin

You’ll start by creating a new application to house the plugin. You may skip this part if you already have an application in which you’d like to create the plugin. Navigate to the Application Builder and complete these steps:

  1. Click Create.
  2. Click Next.
  3. Set Name to APEX jqGrid Plug-in.
  4. Click Create Application.
  5. Click Create Application (on the confirmation page).

Now that the application in place, let’s create the plugin. Navigate to the Shared Components image Plug-ins and follow these steps, as shown in Figure 10-20:

  1. Click Create.
  2. Click Next .
  3. Set Name to APEX jqGrid.
  4. Set Internal Name to COM_CORP_APEX_JQGRID.
  5. Set Type to Region.
  6. Click Create Plug-in.

9781484204856_Fig10-20.jpg

Figure 10-20. Creating the APEX jqGrid plugin

Woo-hoo! You’ve created a plugin, so let’s use it! Navigate to the Page Designer for page 1 and do the following, as shown in Figure 10-21:

  1. Drag and drop the APEX jqGrid [Plug-In] option into the CONTENT BODY area.
  2. Click Save.

    9781484204856_Fig10-21.jpg

    Figure 10-21. Creating an instance of the APEX jqGrid plugin

    Now run the page (you may need to log in) and enjoy your plugin. That didn’t go so well! The error (see Figure 10-22) says you need to define a render function.

    9781484204856_Fig10-22.jpg

    Figure 10-22. The error displayed when running a region plugin without a render function

    I guess I knew you’d have to do some work, so let’s do it. Navigate to Shared Components image Plug-ins, click the APEX jqGrid plugin to access the Create/Edit page for the plugin, and then complete the following steps, as shown in Figure 10-23:

  3. Add the render_apex_jqgrid function from Listing 10-1 to the PL/SQL Source field.
  4. Set Render Function Name to render_apex_jqgrid.
  5. Click Apply Changes.

9781484204856_Fig10-23.jpg

Figure 10-23. The Source and Callbacks regions of the APEX jqGrid plugin

Image Note  Remember to click the help icon on the right of the function name to get the signature expected.

Now try running the page again.

You should finally see the region (Figure 10-24)! Granted, it’s an empty region, but at least you don’t get any errors.

9781484204856_Fig10-24.jpg

Figure 10-24. The empty APEX jqGrid region

File Setup

Now that you have your base plugin created, let’s focus on setting up a directory structure for your files outside APEX. Create a new directory named apex-jqgrid-plugin. Copy the contents of the render_apex_jqgrid function to a new file named plsql_code.sql in the apex-jqgrid-plugin directory. This will serve as a file-based backup of your code. You don’t want to lose all your hard work!

Create a new subdirectory called web-server-files and within that directory create another directory named vendor. You’ll use the web-server-files directory to store all the files that will be added to the web-server-files and vendor subdirectories to house files that are created and maintained by third parties.

Go to www.trirand.com and download the jqGrid plugin with all the options checked. This adds some bloat, but you can always slim things down later when you know what you don’t want to support. Create a new subdirectory named jqgrid-4.8.2 (update the version as needed throughout this tutorial) in vendor and then unzip the contents of the jqGrid plugin you downloaded into that directory. At this point, the directory structure should look like Figure 10-25.

9781484204856_Fig10-25.jpg

Figure 10-25. The file structure for the APEX jqGrid plugin

Now you need to make the jqGrid files available to your plugin in APEX and start using them. Zip up the contents of the web-server-files directory (the child directories, not the web-server-files directory itself) to a zip file named web-server-files.zip. Navigate to the plugin Create/Edit page for APEX jqGrid and complete the following steps:

  1. Click Upload File.
  2. Use File to select the web-server-files.zip file.
  3. Click Upload.

This operation may take a little time because there are quite a lot of files to be loaded within the zip file. Once complete, the Files region of the plugin should look like Figure 10-26. Note that the top-level directory visible is vendor, not web-server-files.

9781484204856_Fig10-26.jpg

Figure 10-26. The Files region (showing only six of many files) after uploading the web-server-files.zip

Initial Grid Render

With the web server files uploaded, you are now ready to use them. Now let’s try getting the jqGrid plugin to render in the most basic way possible. You won’t even worry about the plugin user specifying a query for now. After exploring the jqGrid documentation, you’ll learn the basics of how the jQuery plugin is used and find that it’s possible to populate the grid with local data. You’ll also learn that jqGrid needs some targets in the DOM for placing the grid and the pagination components. Let’s make some changes to get things working. Navigate to the plugin Create/Edit page for APEX jqGrid and follow these steps:

  1. Update the render_apex_jqgrid function in the PL/SQL Source field to the code from Listing 10-2. Don’t forget to update your plsql_code.sql file too.
  2. Create a new JavaScript file named apex-jqgrid-plugin.js with the contents in Listing 10-3. Save the file in the web-server-files directory created earlier.
  3. Return to APEX and click Upload File.
  4. Use File to select the apex-jqgrid-plugin.js file.
  5. Click Upload.
  6. Set Cascading Style Sheet in File URLs to Load to the following:
    #PLUGIN_FILES#vendor/jqgrid-4.8.2/css/ui.jqgrid.css
  7. Set JavaScript in File URLs to Load to the following:
    #PLUGIN_FILES#vendor/jqgrid-4.8.2/js/i18n/grid.locale-en.js
    #PLUGIN_FILES#vendor/jqgrid-4.8.2/js/jquery.jqGrid.min.js
    #PLUGIN_FILES#apex-jqgrid-plugin.js
  8. Click Apply Changes.

As you can see, you added two lines of code to the render function that use sys.htp.p to write to the HTTP buffer; that’s how plugins write out their HTML.

The apex-jqgrid-plugin.js file starts by declaring an array named data that has two elements. The elements represent rows of data that should be displayed in the grid. The next part invokes the jqGrid plugin on the element you added to the DOM via the PL/SQL render function. Finally, you loop over the rows of data you want to show in the grid and invoke the addRowData method of jqGrid to add them.

Run the page and take a look! You should see something like Figure 10-27.

9781484204856_Fig10-27.jpg

Figure 10-27. The initial render of the APEX jqGrid plugin

Adjusting the UI for APEX

Great, you can finally see the grid! However, upon closer inspection, you may notice some things don’t look quite right. For example, hovering over rows shows strange border colors, and the pagination at the bottom doesn’t display the current page correctly. Let’s take a moment to fix those issues now. Navigate to the plugin Create/Edit page for APEX jqGrid and complete the following steps:

  1. Create a new CSS file named apex-jqgrid-plugin.css with the contents in Listing 10-4. Save the file in the web-server-files directory created earlier.
  2. Return to APEX and click Upload File.
  3. Use File to select the apex-jqgrid-plugin.css file.
  4. Click Upload.
  5. Update Cascading Style Sheets in File URLs to Load to include the following:
    #PLUGIN_FILES#apex-jqgrid-plugin.css
  6. Click Apply Changes.

Run the page and test again. Should be looking good!

Getting Set Up for Success

With the styling issues addressed, it’s pretty safe to assume that this jqGrid plugin will meet your needs. Before you continue building out the plugin, let’s take a moment to refactor the code a little so that it’s easier to maintain over time. The first thing you’ll address is the PL/SQL code. Navigate to the plugin Create/Edit page for APEX jqGrid and follow these steps:

  1. Update the render_apex_jqgrid function in the PL/SQL Source field to the code from Listing 10-5. Don’t forget to update your plsql_code.sql file too.
  2. Click Apply Changes.

First you added some basic debug code to the render function. Next you updated the calls that output the HTML to ensure that the elements have unique id attributes. For this you used the region’s static ID with some additional characters to differentiate them. This is the first time you start to make use of the parameters that are passed to you in the PL/SQL functions. Finally, in anticipation of making the plugin a jQuery UI widget, you added just enough JavaScript to load the widget (which will be created next).

If you run the application now, you’ll see that it doesn’t work anymore. This is because the current JavaScript code doesn’t work with the new PL/SQL code. You need to update your JavaScript code so that it creates a jQuery UI widget. Return to the plugin Create/Edit page for APEX jqGrid and do the following:

  1. Update the apex-jqgrid-plugin.js file to the code from Listing 10-6.
  2. Click Upload File.
  3. Use File to select the apex-jqgrid-plugin.js file.
  4. Click Upload.

As you can see, the bulk of the previous code was moved to the _create function of the new widget. Also, the code was modified slightly to accommodate the unique element IDs. Rerun the page and ensure that the plugin works just as it did before.

Bringing on the AJAX

Now that the code has been made a bit more maintainable, let’s set out to do something big: bring in data via AJAX! This is actually a pretty tall order to do all at once, so you’ll break it up into a couple of steps. Let’s start off by creating a baseline AJAX function and call it from your widget in the front end. You know the drill: navigate to the Create/Edit page for APEX jqGrid and complete these steps:

  1. Update the code in the PL/SQL Source field with the code from Listing 10-7. Don’t forget to update your plsql_code.sql file too.
  2. Set AJAX Function Name to ajax_apex_jqgrid.
  3. Click Apply Changes.

The AJAX function just writes a simple JSON object to the HTTP buffer. But to call the function from the front end, you needed to pass the value returned from apex_plugin.get_ajax_identifier to your JavaScript code. The render function was updated to do this via the option object.

Now that the PL/SQL code has been updated, let’s update the JavaScript code to get it in sync. Return to the Create/Edit page for APEX jqGrid and do the following:

  1. Update the apex-jqgrid-plugin.js file to the code from Listing 10-8.
  2. Click Upload File.
  3. Use File to select the apex-jqgrid-plugin.js file.
  4. Click Upload.

You’ve added an ajaxIdentifier property to the options object at the top and set its value to null. This is really just for documentation purposes because the value will be coming from the initialization code in the PL/SQL render function. Also, an AJAX request was made using apex.server.plugin, which is one of the JavaScript APIs that ships with APEX. If the request is successful, then the data returned will be logged to the browser’s console.

Make sure you have the browser’s developer tools open so you can see the console and run the page. If everything worked, you should see the JSON message in the console.

Now that you have a means to send data from the server to the client via AJAX, let’s see whether you can send some dynamic data via a query! Navigate to the Create/Edit page for APEX jqGrid and complete these steps:

  1. Update the ajax_apex_jqgrid function in the PL/SQL Source field with the code from Listing 10-9. Don’t forget to update your plsql_code.sql file too.
  2. Click Apply Changes.

Now you’re using apex_plugin_util to execute a query and apex_json to convert the resultset to JSON. Now when you run the page and view the console, you should see customer data coming across. That’s cool, but how can you get that to display in the grid? After reviewing the demos of the jqGrid online, you’ll learn enough to tackle the JavaScript. Return to the Create/Edit page for the plugin and do the following:

  1. Update the apex-jqgrid-plugin.js file to the code from Listing 10-10.
  2. Click Upload File.
  3. Use File to select the apex-jqgrid-plugin.js file.
  4. Click Upload.

Rather than log the data returned from the server, you now use it to populate the grid. Now when you run the page to test the plugin, you should see data coming from the table, as shown in Figure 10-28.

9781484204856_Fig10-28.jpg

Figure 10-28. Dynamic data loaded in the grid

Using User-Specified Queries

Excellent, you are now loading dynamic data into the grid! Of course, what you really want is to allow the developer using the plugin to specify the query. Let’s start by updating the ajax_apex_jqgrid function to use the query specified in the region’s source. Go to the plugin Create/Edit page for APEX jqGrid and make the following changes:

  1. Update the ajax_apex_jqgrid function in the PL/SQL Source field with the code from Listing 10-11. Don’t forget to update your plsql_code.sql file too.
  2. Check the Region Source is SQL Statement check box in the Standard Attributes region.
  3. Check the Region Source Required check box.
  4. Set Maximum Columns to 50.
  5. Click Apply Changes.

Now you need to move the SQL Query to the region level. Navigate to the Page Designer for page 1 and do the following:

  1. Click the plugin region in the Grid Layout.
  2. Set SQL Query to select cust_first_name, cust_last_name, cust_state from demo_customers.
  3. Click Save.

If you test the page now, it should be working as it was before only now the query is coming from the region, which is dynamic. However, you still have a problem with the column headings because they are still static in the JavaScript. If you were to change the query, things would break. The column model that jqGrid accepts could be created dynamically; you just need a way to get dynamic information related to the columns. This is where the Has “Region Columns” option in the standard attributes comes in. Return to the plugin Create/Edit page for APEX jqGrid and complete these steps:

  1. Check the Has “Region Columns” check box in the Standard Attributes.
  2. Check the Has “Heading” Column Attribute.
  3. Click Apply Changes.

With those attributes enabled, let’s update the region. Go back to the Page Designer for page 1 and do the following:

  1. Click the plugin region in the Grid Layout.
  2. Using the Component Selector (left column), expand the Columns node for the plugin region. If the node cannot be expanded, delete the region and re-create it now that the new options have been added to the plugin. This bug will be fixed in a future version of APEX, and it will not affect users of the plugin.
  3. Select the CUST_FIRST_NAME column.
  4. Using the Property Editor (right column), set Heading to First Name.
  5. Select the CUST_LAST_NAME column.
  6. Set Heading to Last Name.
  7. Select the CUST_STATE column.
  8. Set Heading to State.
  9. Click Save.

Now that the headings for the columns are set with the correct values, you need to pass the values to your jQuery UI plugin. You’ve done this before with the AJAX identifier, so you just need to expand that code to do this the same way, only rather than pass a simple string through for the header, you’ll pass an object that can be extended later. Go to the plugin Create/Edit page for APEX jqGrid and make the following changes:

  1. Update the render_apex_jqgrid function in the PL/SQL Source field with the code from Listing 10-12. Don’t forget to update your plsql_code.sql file too.
  2. Click Save.

That will send the headings through to your jQuery UI widget, but you need to make use of the heading values within that code. Let’s update the apex-jqgrid-plugins.js to do just that. Return to the Create/Edit page for the plugin and do the following:

  1. Update the apex-jqgrid-plugin.js file to the code from Listing 10-13.
  2. Click Upload File.
  3. Use File to select the apex-jqgrid-plugin.js file.
  4. Click Upload.

Notice the column model is now created using the columns array that’s passed in via the options. Now the plugin is completely dynamic! Feel free to modify the query to ensure that it’s working correctly.

Enabling Frozen Columns

Going back to the main feature of the plugin, you need to make it so that some columns can be frozen. Of course, there’s no such standard attribute for this; that’s where custom attributes come in. Let’s add a custom attribute. Navigate to the plugin Create/Edit page for APEX jqGrid and follow these steps, as shown in Figure 10-29:

  1. Click Add Attribute in the Custom Attributes region.
  2. Set Scope to Region Column.
  3. Set Label to Frozen.
  4. Set Type to Yes/No.
  5. Set Default Value to N.
  6. Click Create.

9781484204856_Fig10-29.jpg

Figure 10-29. Creating a Frozen attribute

Now navigate to the Page Designer for page 1 and click one of the columns for the plugin region. You should see the new Frozen attribute under Settings in the property editor (see Figure 10-30).

9781484204856_Fig10-30.jpg

Figure 10-30. New Frozen attribute in the column properties

Set that to Yes for one or two columns and then click Save. You know what’s next: you have to map the value through from the PL/SQL render function to the JavaScript widget and then utilize the value in the widget. Navigate to the Create/Edit page of the plugin and update the PL/SQL.

  1. Update the render_apex_jqgrid function in the PL/SQL Source field with the code from Listing 10-14. Don’t forget to update your plsql_code.sql file too.
  2. Click Apply Changes.

Now that the column’s frozen Y/N value is being passed through to the JavaScript widget, let’s adjust the JavaScript code to make use of the new attribute. Return to the Create/Edit page for the plugin and complete the following steps:

  1. Update the apex-jqgrid-plugin.js file to the code from Listing 10-15.
  2. Click Upload File.
  3. Use File to select the apex-jqgrid-plugin.js file.
  4. Click Upload.

As you can see, you first declare two arrays to hold and separate out the frozen columns from those that are not frozen. This is done so that you can concatenate them back together later, placing the frozen columns first, as is required by jqGrid. Later, the frozenCols array is also used to determine whether a call to setFrozenColumns should be made on the grid.

Now run the page to give it a shot. Keep in mind that you’ll need to have a lot of columns to see the full effect. Try changing the query for the region to select * from demo_customers for an easy way to test this.

With that code in place, you have achieved the major goal of this plugin. But as you can imagine, there is still much work to be done. There are still many attributes that must be mapped through, both at the grid/region level as well as at the column level. You’re not going to do them all here because that would be the perfect exercise for you to get some practical experience. Here’s a list of some of the more obvious mappings that should be done.

Standard attributes:

  • Has “Heading Alignment” column attribute
  • Has “Alignment” column attribute

Custom region attributes:

  • Rows Per Page

Custom column attributes:

  • Width
  • Height
  • Shrink Columns to Fit (Yes/No)

Another enhancement worth considering is “true” pagination. Currently all the rows are fetched from the query specified for the region, and pagination is done on the client side. This works well when there aren’t too many rows (maybe a few thousand at most), but for larger datasets this will not perform so well. True pagination would be tricky because it would have to be customized, as APEX doesn’t support the type of URLs that jqGrid expects. You will not implement true pagination in this chapter because it would focus less on the plugin architecture and more on jqGrid customization.

Securing the Plugin

Another issue to consider, and one that I will address because of its importance, is security. Currently your plugin is vulnerable to XSS attacks because you are not escaping the content returned from the queries. Also, you are not paying any attention to the is_displayed property of the columns that lets you know whether a column should be suppressed because of conditions or authorization schemes used on the column.

Let’s start by addressing the XSS vulnerability. Navigate to the plugin Create/Edit page for APEX jqGrid and do the following:

  1. Check the Has “Escape Special Characters” column attribute check box.
  2. Update the ajax_apex_jqgrid function in the PL/SQL source field with the code from Listing 10-16. Don’t forget to update your plsql_code.sql file too.
  3. Click Apply Changes.

You check the escape_output property of each column, and if it’s true, you use apex_escape.html to escape the HTML output. The default value for this attribute will be true, which is part of the “secure by default” approach that APEX provides. If developers want to allow HTML markup through for some reason, then they’ll have to manually set that attribute to No.

Now you should be able to run the page, and if there’s any HTML markup in the grid data, it should be escaped automatically. If the escaping doesn’t work for you, delete the region and re-create it now that the new attribute has been added. This bug will be fixed in a future version of APEX, and it will not affect users of the plugin.

Next you’ll leverage the is_displayed property of the columns to prevent them from showing when they shouldn’t. To do this correctly, you’ll need to update both the render and AJAX functions in the PL/SQL. One last time, navigate to the Create/Edit page of the plugin and complete the following steps:

  1. Update the PL/SQL Source with the code from Listing 10-17. Don’t forget to update your plsql_code.sql file too.
  2. Click Apply Changes.

As you can see, both functions are now referencing the is_displayed property of the columns to conditionally display them. It may have been tempting to just update the render function because that could have prevented the column data from having been displayed in the grid. However, to be truly secure, the data that should never be transferred to the client. That’s why the ajax_apex_jqgrid function was updated too.

That’s it; you’ve created a region plugin! Ideally this tutorial has demonstrated the iterative approach that one typically uses when building a plugin. At this point, you could export your plugin and share it with the world.

I recommend adding the plugin export file to the top level of your plugin files (in apex-jqgrid-plugin). Then you can zip up the entire directory structure and put it somewhere others can find it. If possible, put your source code on GitHub. Ideally others will not only use your hard work but help you make it better as well!

Best Practices for Developing Plugins

The best practices covered in this section have been compiled from a variety of sources. Some are evangelized by the same people who wrote the APEX plugin framework—they know their stuff. Others are based on well-established “best practices” from related technologies such as the Oracle Database and web development. I’ve even added a few practices I’ve learned while developing and maintaining several successful plugins over the past few years. While intended to help, “best practices” may not always be the best solution for every situation, so always test!

These are some best practices for plugins in general:

  • Follow APEX standards when possible. Over the years the APEX team has implemented a number of standards used for native components. For example, items that render multiple elements on the page follow a standard naming convention. Learning about and adopting these standards in your own plugins will help maintain a level of constancy in APEX and help to ensure that your plugins work correctly.
  • Choose the Name value wisely. Choosing a “good” name for your plugin is important. Your plugin should be easily distinguishable from others. Some plugin developers have adopted the practice of including their company name to help ensure uniqueness (and add a shameless plug, of course).
  • Choose the Internal Name setting wisely. The Internal Name setting is used to determine whether a new plugin is being installed or an existing plugin is being replaced. For this reason, uniqueness of the Internal Name setting is even more important than the Name setting. A good convention that helps to ensure uniqueness is to base the Internal Name setting on your company name/URL, as is often done in Java class naming. An example would be COM_CORP_CALENDAR. Also, if you make changes to a plugin that are not compatible with previous versions, changing the Internal Name setting will allow plugin users to “migrate” to the new plugin without breaking existing instances. Consider appending a letter to the end of the Internal Name setting that is incremented each time there is a compatibility issue with the new release, as in COM_CORP_CALENDAR_B.
  • Document, document, document. Having developed your own plugin, everything about it will be second nature to you. But it will not be as intuitive to others. The only solution is good documentation. Documentation should not be considered optional if you want heavy adoption. If you’re more comfortable writing code than documentation, reach out to someone for help. Consider adding help in the following areas: the Help section of the plugin, the Help section of custom attributes, and a complete help file bundled with the plugin (TXT, RTF, HTML, and PDF all work well).

These are some best practices for JavaScript and CSS:

  • Use the APEX JavaScript APIs when appropriate. Because jQuery is now included with APEX, it may be tempting to use the val() function to set the value of an element on the page. However, the $v and $s functions, part of the JavaScript APIs included with APEX, were created to work with APEX items specifically so they handle things like LOVs correctly. Newer, properly namespaced APIs are available as well. For example, apex.item(’PX_ITEM’).getValue() can be used in place of $v(’PX_ITEM’). It’s a bit more verbose, but by using only the functions in the APEX namespace, you can prevent issues that could result from collisions with other JavaScript libraries.
  • Trigger events when appropriate. A number of events were introduced with APEX 4.0, such as apexbeforerefresh and apexafterrefresh. Ensuring that your plugins trigger these events when they apply allows plugin users to create dynamic actions on top of them. Also, depending on the plugin, you may want to consider triggering custom events and registering them with the plugin so that they are available via dynamic actions.
  • Compress your JavaScript and CSS code. The JavaScript and CSS code used in your plugins will add to the overall page weight in APEX. To minimize the impact on performance, make sure to compress the code before deploying the plugin. There are many open source libraries available for compressing JavaScript and CSS; many are made to work with task runners such as Grunt or Gulp, which can help automate the process. Make sure to keep the original files safe for future development because compressed files are not very usable.
  • Use files accessible via the file system of the web server. The APEX plugin framework makes it easy to “bundle” files with plugins by uploading them directly as part of the plugin. This is convenient for both installation and deployments because the files go with the plugin. However, the files are stored in the database, which adds a little overhead when the browsers go to retrieve them. Using files on the file system avoids this overhead but requires additional installation and deployment steps. Try to design your plugins in such a way that plugin files are bundled by default, but switching to file system files requires little effort.
  • Protect references to the $ object for jQuery. Now that jQuery and jQuery UI are included with APEX, you may want to take advantage of them in your plugins. Many people who already use jQuery are familiar with referring to the jQuery object as $. While convenient, this practice can cause problems if another JavaScript library is using the $ as well. See the “jQuery UI Widget Factory” section for a working example of how to protect references to the $ object.
  • Use debug/logging code in your code. Debug code is code that is added to code to provide insight into how the code is (or is not) working. Debug code can be useful to plugin developers as well as plugin users. For JavaScript code, APEX now includes the apex.debug namespace, which has different methods for logging based on log levels such as trace, warn, and error.

For PL/SQL, use these best practices:

  • Use compiled code when appropriate. When the source code of a plugin is embedded in its PL/SQL code attribute, it is treated as an anonymous block and must all be compiled each time any one of the callback functions is executed. Using compiled code—code that has been placed in a PL/SQL package, for example—can avoid this overhead. This is especially important for plugins that use a lot of PL/SQL. However, plugin developers must keep in mind that this technique requires additional installation steps for plugin users. Try to design your plugins in such a way that plugin users can conveniently start using the plugin but can easily move to more performant code if needed.
  • Escape user input when appropriate. Plugins will often display data that is maintained by end users. If the data is not properly escaped, the plugin could introduce cross-site scripting (XSS) vulnerabilities in an application. Using APEX_ESCAPE.HTML to escape special characters whenever working with user-maintained data is the best way to protect against XSS. However, because this may not always be the desired functionality, you may want to make it optional. See the “Creating a Region Tutorial” section for a working example of using APEX_ESCAPE.
  • Use debug/logging code. Debug code can be useful to plugin developers as well as plugin users when it comes to hunting down and fixing bugs. See the “Creating a Region Tutorial” section for a working example of using APEX_PLUGIN_UTIL to output some basic debug information when the application was running in debug mode. Consider using the APEX_DEBUG package to add additional debug information in your plugins where appropriate.
  • Use named notation as much as possible. In PL/SQL, parameters can be passed using positional notation, named notation, or a combination of both. While it may not make sense to always use named notation, using it as much as possible will help to self-document your code and possibly prevent repeated readings of the API documentation.

Summary

In the first part of this chapter, you learned about the various parts of the plugin architecture in APEX as well as some other tools that can help with plugin development, such as the jQuery UI Widget Factory. In the second part of the chapter, you put what you learned in the first part to use as you built a jqGrid region plugin. Many techniques, from creating custom attributes to fetching data via AJAX, were touched on along the way. Finally, some “best practices” were covered to help you create high-quality plugins. At this point, you should be armed with enough knowledge to begin development on your own plugins.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset