23 July 2010

APEX 4.0 Plugin: Watermark


This simple plugin for APEX 4.0 shows some text inside an item. Usually a watermark is used to give the user extra information to assist with filling out a Form. Creating this plugin was more a learning experience than anything else. If you just want the plugin than follow this link to download it. If you want to go through the same learning experience I went through while creating this plugin, than keep reading.

Most of the APEX 4 Plugin tutorials that I encountered are structured like "click here", "copy this". To me this doesn't aid in my understanding of the plugin mechanism. Needless to say that plugin tutorials written for APEX 4 EA (Early Adapter) are less effective, since the interface for creating plugin has changed. Currently the only way to learn how to create a plugin is going through someone else's plugin code and try to figure out what it is they did. Which can be quite challenging.

Enough for the rant. Time to get back to work.

Step 1. Download the jQuery plugin.
For this plugin, I used the Watermark jQuery plugin. On this page you can also see how the plugin should be used.
Step 2. Create a new plugin in APEX
Go to the Shared Components of your application and choose "Plug-ins" in the "User Interface" section.

On the Plugin overview page, click the CREATE button. Fill out the "Name" and "Internal Name" items, choose "Item" for Type and click the CREATE button. Why click the button now? Well, it turns out that the type of plugin can reveal extra properties on this page. So, if you're follow other "click here - copy this"-tutorials make sure to click the CREATE button after you filled out the name, internal and type.

  • Name: Watermark

  • Internal Name: nl.amis.watermark

  • Type: Item


Step 3: Upload the javascript library
To upload the javascript, go to the files section of the plugin edit/create form, and follow the wizard to upload the javascript file. As easy as that. Why do you need to do this? The plugin contains all necessary information to be exported into other applications. As this plugin needs the javascript library, include it with the plugin.
Step 4: Custom Attributes
What do you need to define the watermark? First of all some text, because that is the text that will be shown as the watermark. Second, the plugin allows you to define your own color instead of the standard light grey.
Go to the section labelled "Custom Attributes" and create two attributes:
First:

  • Scope: component

  • Display sequence: 10

  • Label: Watermark

  • Type: Text

  • Required: Yes

  • Help Text: The text that you want to appear as a Watermark in the item.


Second:

  • Scope: component

  • Display sequence: 20

  • Label: Color

  • Type: Text

  • Required: No

  • Default Value: #369

  • Help Text: The color of the Watermark. Default is #369, which is a light grey.


Why do you need to do this? You need to create some attributes to define the appearance of the plugin, in this case the text and the color of the text, giving some flexibility to the plugin.
Step 5: Create a Render function
The Render function is the code that generates the plugin on the form. What it needs to render is determined by the type of plugin, in this case an Input item. The signature of the render function looks like this

function <name of function> (
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

Tip: To get the signature of the render function, click on the "Render Function Name" in the Callback section of the Plugin create/edit page.
In order for the plugin to render correctly, you need to adhere to this signature.

Using the HTP package, it's fairly easy to create an Input item.
What you need to construct is something like this

sys.htp.p ('<input type="text" name="'||l_name
||'" id="'||p_item.name||'" '
||'value="'||l_value
||'" size="'||p_item.element_width
||'" '||'maxlength="'||p_item.element_max_length
||'" '||p_item.element_attributes||' />');

Watch for the quotes and double quotes!

In the code above there is a reference to a local variable, named "l_name". In order to get the name attribute of the input item, you need to call the Apex_Plugin package:

l_name := apex_plugin.get_input_name_for_page_item (false);

The APEX_PLUGIN is actually a synonym for the WWV_FLOW_PLUGIN package. Reading the note from the package specification shows that you need this function before using the HTP package:

--==============================================================================
-- Returns the name attribute which has to be used for a HTML input element if
-- you want that the value of the element is stored in session state when the
-- page is submitted. If you have a HTML input element which returns multiple
-- values (eg. select list with multiple="multiple") you have
-- to set p_is_multi_value.
-- Note: This function has to be called before you write something to the
-- HTTP buffer with HTP.P(RN)
--==============================================================================
function get_input_name_for_page_item (
p_is_multi_value in boolean )
return varchar2;


Two more things to do in constructing the rendering function, include the javascript library and write the jQuery code to "attach" the watermark to the item.
Including the javascript library is as easy as calling a package,...

apex_javascript.add_library (p_name => 'jquery.watermarkinput'
,p_directory => p_plugin.file_prefix
,p_version => null
);

Note: Do not include the extension of the javascript library. In other words: omit the ".js" extension.
"Attaching" (by lack of better term) the watermark to the item, we need to refer to the documentation that goes with the Watermark plugin. This is what the render function needs to generate as "on Load" code:

jQuery(function($){
$("#suffix").Watermark("Suffix","#369");
});

Using the Apex_Javascript package allows you to add this code to the "document ready" function. If you are not familiar with this term, forget about it. Just remember that the jQuery code is executed after the page is shown and all good things happen there. Want to know more? Google: "jQuery document ready" and you will find lots of tutorials on using this function.
The only thing we need to generate is the line which starts with the dollar ($) sign.
Something like this:

apex_javascript.add_onload_code (p_code =>
'$("#'||p_item.name||'").Watermark("'||l_watermark||'","'||l_color||'");'
);

Watch those quotes and double quotation marks again.

Now the complete rendering function looks like this, including some debug information. Notice the use of the Custom Attributes (defined as p_item.attribute_01 and p_item.attribute_02):

function render_watermark (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
is
l_value varchar2(32767) := sys.htf.escape_sc (p_value);
l_name varchar2(30);
l_watermark apex_application_page_items.attribute_01%type := p_item.attribute_01;
l_color apex_application_page_items.attribute_02%type := coalesce (p_item.attribute_02, '#369');
retval apex_plugin.t_page_item_render_result;
begin
if apex_application.g_debug
then
apex_plugin_util.debug_page_item (p_plugin => p_plugin
,p_page_item => p_item
);
end if;
l_name := apex_plugin.get_input_name_for_page_item (false);
if p_is_readonly or p_is_printer_friendly
then
if p_is_readonly and not p_is_printer_friendly
then
sys.htp.p ('<input type="hidden" name="'||l_name||'" '||
'id="'||p_item.name||'" value="'||l_value||'" />'
);
end if;
sys.htp.p ('<span id="'||p_item.name||'_DISPLAY" '||
coalesce (p_item.element_attributes, 'class="display_only"')||
'>'||l_value||''
);
else
sys.htp.p ('<input type="text" name="'||l_name||'" id="'||p_item.name||'" '||
'value="'||l_value||'" size="'||p_item.element_width||'" '||
'maxlength="'||p_item.element_max_length||'" '||
p_item.element_attributes||' />');
apex_javascript.add_library (p_name => 'jquery.watermarkinput'
,p_directory => p_plugin.file_prefix
,p_version => null
);
apex_javascript.add_onload_code (p_code =>
'$("#'||p_item.name||'").Watermark("'||l_watermark||'","'||l_color||'");'
);
end if;
return retval;
end render_watermark;

I also set some standard attributes as depicted below.

Step 6: Validation
Now we are almost done. You could try it out now. But there is a catch,... What do you think gets stored in the database if you don't enter a value for the item? Exactly, the watermark text that you provide. In order to prevent that from happening, I searched for ways to use the functionality that the Watermark plugin provides.

function UseData(){
$.Watermark.HideAll();
//Do Stuff
$.Watermark.ShowAll();
}

But was unable to find the correct location where to add this. So I decided to implement a "validation" function.
In the Callback section, where you also specify the render function, add a validation function - in this case call it "validate_watermark".
The function is not really a validation functionality, the only thing that it does is set the session state for the item to NULL - only when the value of the item is equal to the watermark text.

function validate_watermark (p_item in apex_plugin.t_page_item
,p_plugin in apex_plugin.t_plugin
,p_value in varchar2
)
return apex_plugin.t_page_item_validation_result
is
retval apex_plugin.t_page_item_validation_result;
l_watermark apex_application_page_items.attribute_01%type := p_item.attribute_01;
begin
if apex_application.g_debug
then
apex_plugin_util.debug_page_item (p_plugin => p_plugin
,p_page_item => p_item
);
end if;
if p_value = l_watermark
then
-- Set session state to NULL for the item
apex_util.set_session_state(p_item.name, null);
end if;
return retval;
end validate_watermark;


Creating a simple plugin like this one is not very hard. Writing this blog took a lot more time than creating the actual plugin. The hardest part was trying to figure out what exactly is needed to render the item and how to fit the jQuery samples in the PL/SQL code.

Links
Watermark by Josh Bush (Digital Bush)
Easy reference for HTML Color Codes
A lot more plugins for APEX
jQuery homepage

3 comments:

  1. gr8 post Alex, really helpfull

    ReplyDelete
  2. great intro - thx for sharing

    ReplyDelete
  3. That's nice information about plugins! Good technical details too. Congrats for your blog!

    ReplyDelete