OOUI/Elements/Lookup

OO.ui.mixin.LookupElement is a mixin that creates a menu of suggested values for a text input widget. Suggested values are based on the characters the user types into the text input field and, in general, the menu is only displayed when the user types (unlike the menu in a OO.ui.SearchWidget, which is always visible). If a suggested value is chosen from the lookup menu, that value becomes the value of the input field.

Note that a new menu of suggested items is displayed when a value is chosen from the lookup menu. If this is not the desired behavior, disable lookup menus with the LookupElement's setLookupsDisabled() method, then set the value, then re-enable lookups.

To use LookupElement, you must implement its three abstract methods:

  1. getLookupDisabled() — Return a request object that will be used to retrieve the suggested values. This should be a promise object, such as is returned by mw.Api.get().
  2. getLookupCacheDataFromResponse() — Take the response from the above request and do whatever processing is required before it is cached locally.
  3. getLookupMenuOptionsFromData(data) — Take the processed data from the above (which may have been cached) and turn it into an array of OO.ui.MenuOptionWidget objects.

Example

edit

The following is an example of a TextInputWidget that mixes in LookupElement to generate a lookup menu (live demo):

 

It is used inside a dialog window and customized with the $overlay config so that it can extend beyond the edges of the dialog window. If the $overlay config were not specified, the lookup menu would be clipped by the dialog window. See OOUI/Concepts#Overlays for details.

// An example of a text input widget that mixes in lookup element
function MyLookupTextInputWidget( config ) {
 OO.ui.TextInputWidget.call( this, { validate: 'integer', placeholder: 'Enter an integer' }  );
 OO.ui.mixin.LookupElement.call( this, config );
}

OO.inheritClass( MyLookupTextInputWidget, OO.ui.TextInputWidget );
OO.mixinClass( MyLookupTextInputWidget, OO.ui.mixin.LookupElement );
/**
 * Get a new request object of the current lookup query value.
 *
 * @protected
 * @method
 * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
 */
MyLookupTextInputWidget.prototype.getLookupRequest = function () {
  var
    value = this.getValue(),
    deferred = $.Deferred(),
    delay = 500 + Math.floor( Math.random() * 500 );

  this.getValidity().done( function ( valid ) {
    if ( valid ) {
      // Resolve with results after a faked delay
      setTimeout( function () {
        deferred.resolve( [ value * 1, value * 2, value * 3, value * 4, value * 5 ] );
      }, delay );
    } else {
      // No results when the input contains invalid content
      deferred.resolve( [] );
    }
  } );

  return deferred.promise( { abort: function () {} } );
};
/**
 * Pre-process data returned by the request from #getLookupRequest.
 *
 * The return value of this function will be cached, and any further queries for the given value
 * will use the cache rather than doing API requests.
 *
 * @protected
 * @method
 * @param {Mixed} response Response from server
 * @return {Mixed} Cached result data
 */
MyLookupTextInputWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
  return response || [];
};
/**
 * Get a list of menu option widgets from the (possibly cached) data returned by
 * #getLookupCacheDataFromResponse.
 *
 * @protected
 * @method
 * @param {Mixed} data Cached result data, usually an array
 * @return {OO.ui.MenuOptionWidget[]} Menu items
 */
MyLookupTextInputWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
  var
    items = [],
    i, number;
  for ( i = 0; i < data.length; i++ ) {
    number = String( data[ i ] );
    items.push( new OO.ui.MenuOptionWidget( {
      data: number,
      label: number
    } ) );
  }

  return items;
};

function MyDialog( config ) {
   MyDialog.super.call( this, config );
}
OO.inheritClass( MyDialog, OO.ui.Dialog );
MyDialog.static.name = 'MyDialog';
MyDialog.static.title = 'Using the lookup element mixin';
MyDialog.prototype.initialize = function () {
  MyDialog.super.prototype.initialize.apply( this, arguments );
  var panel = new OO.ui.PanelLayout( { padded: true, expanded: false } ),
    lookup = new MyLookupTextInputWidget( { $overlay: this.$overlay } );
  panel.$element.append( lookup.$element, '<p>This dialog contains a text input widget that ' +
    'mixes in LookupElement to generate a lookup menu. By default, the menu uses relative positioning. ' +
    'In this example, the $overlay config is passed to the constructor to allow the menu to extend beyond' +
    ' the edge of the dialog.  If this config were not specified, the lookup menu would be clipped by' +
    ' the dialog window.</p>' );
  this.$body.append( panel.$element );
};

var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
var dialog = new MyDialog();
windowManager.addWindows( [ dialog ] );
windowManager.openWindow( dialog );

For a full list of supported methods, please see the code-level documentation for LookupElement.