Advanced Widget Example

In this article we will create Google Search Widget based on Google AJAX Search API. First of all lets look at this document to find out what Google offers. As you can see Google returns JSON response that we will use for implementing Google Search Widget.

Lets begin with creating required files. As you may remember from hello world example there are one required folder and two required files, but for this example we will use additional CSS file. Look at the picture:

Now open config.xml file and copy and paste following xml code:

<?xml version="1.0" encoding="UTF-8"?>
<widget name="googlesearch" icon="google" section="search">
    <title>Google Search</title>
    <description>
        <![CDATA[Google Search Widget]]>
    </description>
    <tags>
        <![CDATA[google, search]]>
    </tags>
</widget>

Note that configuration file is not required for development version.

Thats it, we are done with configuration. Now we need to define our goals. First of all lets decide what user interface component we need, these are:

  1. HTML form with input field and button to submit queries
  2. Search result area where returned result will be displayed
  3. Pagination control because returned result can be quite long

Now based on defined requirements lets create widget skeleton and setup it:

(function() {
 
    function GoogleSearch() {
 
        /**
         * create initial configuration
         */
        this.config = {
            name: 'googlesearch',   //widget name
            title: 'Google Search', //widget title
            iconId: 'google',       //icon identifier
            css: true               //tell widget engine that it needs to load widget's CSS file
        };
 
        /**
         * onload event will be triggered after widget completed loding
         * perform user interface setup here
         */
        this.onload = function() {
 
            /**
             * create HTML form with ascribed input elements and result area
             */
            this.setContent(
                '<form method="post" action="" class="google-search-form">' +
                    '<input type="text" name="q" />' +
                    '<input type="button" name="search" value="Search" />' +
                '</form>' +
                '<div class="google-search-results">Nothing to show...</div>'
            );
 
            /**
             * setup Pagination UI component
             */
            var pagination = UDW.Components.Pagination.getComponent(
                {
                    currentPage: 1,            //starting page
                    rowsPerPage: 4,            //number of results per page
                    rowName: 'results',        //name of the collection in the result-set
                    callback: function(rows) { //callback function to process result-set
 
                        var html = [];
			html.push('<ul>');
 
                        for (var i = 0; i < rows.length; i++) {
                            html.push('<div class="google-search-link">');
                            html.push('<a href="' + rows[i].url + '" target="_blank">' + rows[i].title + '</a>');
                            html.push('</div>');
                            html.push('<div class="google-search-content">' + rows[i].content + '</div>');
                            html.push('<div class="google-search-visible-url">' + rows[i].visibleUrl + '</div>');
                        }
 
                        html.push('</ul>');
 
                        //push generated HTML code to search result area
                        $('div.google-search-results', widget.getBody()).html(html.join(''));
 
                    }
                }
            );
 
            //add pagination to the widget's footer section
            this.addToFooter(pagination);
 
        };
 
    };
 
    UDW.registerWidget('googlesearch', GoogleSearch);
 
})();

Now, if you try to load the widget you will see something like this:

Widget's UI is completed, now we can continue to coding widget's search logic. First of all it is necessary to bind callback function to the search button. This goal can be achieved with following code:

$('input[type=button]', widget.getBody()).on('click', function() {});

Code just searches “button” and binds callback function to its “onclick” event. Now we can begin coding of actual search logic:

 
//obtain reference to the widget
var widget = this;
 
//bind function to button's onclick event
$('input[type=button]', widget.getBody()).on('click', function() {
 
    //google search URI	
    var uri = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&q={query}';
 
    //build request URI
    uri = uri.replace('{query}', this.form.q.value);
 
    //find result area
    var body = $('div.google-search-results', widget.getBody());
 
    //set pre-loader to the result area
    body.html(widget.getPreloader());
 
    /**
     * use getJSON API method to perform Ajax request
     */
    UDW.proxy.getJSON(
        {
            uri: uri, //uri to be requested
            callback: function(result) { //callback function
                //process response data
                pagination.process(result.responseData);
            }
        }
    );
 
})

As you see from the code above search logic is quite simple. All you need is to use “getJSON” API method with to parameters uri and callback. After request completes callback function is invoked that on other hand invokes Pagination component's “process” method to process returned results.

Example shows how simple and powerful API is. Most of the code is just using predefined API methods. If you completed coding you can see that resulting widget looks something like this:

Complete JavaScript code and CSS required for this widget is shown below:

googlesearch.js

(function() {
 
    function GoogleSearch() {
 
        this.config = {
            name: 'googlesearch',
            title: 'Google Search',
            iconId: 'google',
            css: true
        };
 
        this.onload = function() {
 
            this.setContent(
                '<form method="post" action="" class="google-search-form">' +
                    '<input type="text" name="q" />' +
                    '<input type="button" name="search" value="Search" />' +
                '</form>' +
                '<div class="google-search-results">Nothing to show...</div>'
            );
 
            var pagination = UDW.Components.Pagination.getComponent(
                {
                    currentPage: 1,
                    rowsPerPage: 2,
                    rowName: 'results',
                    callback: function(rows) {
                        var html = [];
                        html.push('<ul>');
                        for (var i = 0; i < rows.length; i++) {
                            html.push('<div class="google-search-link">');
                            html.push('<a href="' + rows[i].url + '" target="_blank">' + rows[i].title + '</a>');
                            html.push('</div>');
                            html.push('<div class="google-search-content">' + rows[i].content + '</div>');
                            html.push('<div class="google-search-visible-url">' + rows[i].visibleUrl + '</div>');
                        }
                        html.push('</ul>');
                        $('div.google-search-results', widget.getBody()).html(html.join(''));
                    }
                }
            );
 
            this.addToFooter(pagination);
 
            var widget = this;
 
            $('input[type=button]', widget.getBody()).on('click', function() {
 
                var uri = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=large&q={query}';
                uri = uri.replace('{query}', this.form.q.value);
 
                var body = $('div.google-search-results', widget.getBody());
                body.html(widget.getPreloader());
 
                UDW.proxy.getJSON(
                    {
                        uri: uri,
                        callback: function(result) {
                            pagination.process(result.responseData);
                        }
                    }
                );
 
            })
 
        };
 
    };
 
    UDW.registerWidget('googlesearch', GoogleSearch);
 
})();

googlesearch.css

form.google-search-form {
    width: 100%;
    text-align: center;
    margin: 4px 0;
}
 
div.google-search-results {
    border: 1px solid #dedede;
    padding: 6px;
    margin: 12px 0 0 0;
}
 
div.google-search-link a {
    color: blue;
}
 
div.google-search-content {
    margin: 2px 0;
}
 
div.google-search-visible-url {
    color: green;
    margin-bottom: 8px;
}