User login

I recently had a need to create linked autocomplete fields for one of my clients. The use case was pretty common; there was a list of companies and then a list of contacts, each one associated with a company. The goal was to have the customer service rep select the company, then have the list of contacts filtered by the company.

Each contact was a user in the system. We used the Content Profile module to collect additional contact information and added a nodereference field to link the contact to the company. We also decided to use the Realname and Realname Userreference modules to make it easier to find contacts.

Autocomplete Adventure

Drupal has autocomplete functionality built in, and it is fairly simple to create an autocomplete field. It becomes a little more challenging when you need to modify an existing autocomplete field.

I started by trying to use hook_form_alter to change the autocomplete path, but that property was not available to me in the form elements. My only option was to use javascript to modify the autocomplete path after the page loaded. Thanks to this article at Digital Illusion and a slightly older tutorial from d.o I was able to piece together my final solution.

First, I needed to replace the built in autocomplete url with my own:

//globals
var $clientField;
var $contactField;
$(document).ready(function(){	
	$clientField = $("#edit-field-ticket-client-0-nid-nid");
	$contactField = $('#edit-field-ticket-contact-0-uid-uid');
	$contactAcField = $("#edit-field-ticket-contact-0-uid-uid-autocomplete");
	var url = $contactAcField.val();
	url = url.replace('realname_userreference/autocomplete/field_ticket_contact','native_support_ticket/username/autocomplete/field_ticket_contact/');
	var input = $contactField.attr('autocomplete', 'OFF')[0];
	recreateAutoComplete(input, url);
});

function recreateAutoComplete(input, url) {
	$(input).unbind();
	var acdb = new Drupal.ACDB(url);
	$(input.form).submit(Drupal.autocompleteSubmit);
	new Drupal.jsAC(input, acdb);
	
	//focus is used on the child field because contact field can be
	//entered while changing a node and never toucing the parent field.
	$("#edit-field-ticket-contact-0-uid-uid").focus(function() {
		clientSelected($clientField);
	});
}

And the javascript to update the contact field's autocomplete url when the company field changes:

function clientSelected(parent) {
  // Get the url from the child autocomplete hidden form element
  var url = $contactAcField.val();
  // Alter it according to parent value parsing the node id from the nodereference field
  var regex = /\[nid:(.*)\]/;
  var regex_results = regex.exec(parent.val());
  url = url.replace('realname_userreference/autocomplete/field_ticket_contact','native_support_ticket/username/autocomplete/field_ticket_contact/');
  url += regex_results[1];
  var input = $contactField.attr('autocomplete', 'OFF')[0];
  // Recreate autocomplete behaviour for the child textfield
  var input = $('#edit-field-ticket-contact-0-uid-uid').attr('autocomplete', 'OFF')[0];
  recreateAutoComplete(input, url);
}

In my module, I created a new menu item in hook_menu:

   $items['native_support_ticket/username/autocomplete'] = array(
    'title' => 'Native Support Userreference autocomplete',
    'page callback' => 'native_support_userreference_auto',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
  );

 

The autocomplete function itself isn't very interesting since it is essentially the same as the function from Realname Userreference. I just modified the SQL statement to filter the results by company node id.
 
The real surprise for me is that there isn't an easier way to link fields like this in a parent child relationship. My next task will be to see if there is some way to generalize this out into a module to make the functionality more widely available. I am open to any suggestions, or easier methods of accompolishing this task if possible.

Comments

Post new comment