Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

General Questions /

General questions about getting started with SilverStripe that don't fit in any of the categories above.

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

(solved) Autocomplete TextFields: How can you get it to work?


Go to End


21 Posts   7446 Views

Avatar
martimiz

Forum Moderator, 1391 Posts

17 March 2011 at 10:45am

Hi there,
I just found out that some errors found their way into the log - happened when I indeed renamed the AutoCompleteField class to BalbusAutoCompleteField to fix the conflict with the existing SilverStripe AutoCompleteField, but failed to correct all occurances of the fieldname in the documentation. Real sorry about that :-(

The only thing you need to change is the name of the files to BalbusAutocompleteField.php and BalbusAutocompleteField.js and then create the field by its proper name:

$fields->addFieldsToTab('Root.Content.Main', array(
new BalbusAutocompleteField(
$name = 'MyField',
$title = 'Enter a value'
)
));

And no, you don't have to do it that way, any correct way of adding a formfield to the CMS should do. Let me know if you run into any new problems...

Avatar
BlueScreen

Community Member, 36 Posts

17 March 2011 at 12:21pm

Okay, after changing all instances of 'autocompletefield' to 'balbusautocompletefield', that seemed to resolve the conflicts, I can confirm the correct code is being run accordingly. However I am now back to where I was before: The autosuggest cannot seem to find anything

EG: i type in 'pa' which should pick up 'paris' from this list:

    function suggest() {
		
		$return_arr = array(
			array('value' => 'London'),
			array('value' => 'Paris'),
			array('value' => 'Rome')
		);
		return json_encode($return_arr);
    }

The result is just a long stream of unanswered GET requests, but perhaps that is normal behavior if the autosuggest naturally cannot find a reference list to suggest from?

Avatar
BlueScreen

Community Member, 36 Posts

17 March 2011 at 2:11pm

Edited: 17/03/2011 2:45pm

My suspicions were correct. The function 'suggest()' is not being executed and therefor the autosuggest has no reference list to begin with.

Not sure why at this point but that explains quite a bit.

Add this:

user_error("breakpoint:", E_USER_ERROR); 

inside the suggest() function. Does it successfully break for you?

EDIT:

"The url adds the url segment suggest to the AutocompleteField's link, using it as a simulated page url with param Action=Suggest. This automatically calls the AutocompleteField's suggest() method."

Well it seems the correct url is being outputted (It returns a 404 if it wasn't) but it cannot access suggest() anyway. maybe there are permissions in the way or the redirect rules are screwing up the url. Either way something is stopping the url from calling suggest()

Direct links (EG: $attributes['src'] = 'bookingmanager/code/balbusautocompletefield/suggest';) don't work either, damn

Avatar
martimiz

Forum Moderator, 1391 Posts

18 March 2011 at 12:40am

Edited: 18/03/2011 12:42am

I know this is only a very basic autocomplete field. Thanks for your comments - it does set me to working on this thing again - and I did find a couple of issues :-( So if you are willing to take this a step further, this would be the first test:

Preferrably on a fresh install, in the Page class add:

public static $db = array(
  'Test' => 'Varchar'
);

function getCMSFields() {
	$fields = parent::getCMSFields();

	...

	$fields->addFieldsToTab('Root.Content.Autocomplete', array(
		new BalbusAutocompleteField (      
			$name = 'Test',
			$title = 'Autocomplete testfield'
		)
	));

	return $fields;
}

In the BalbusAutoCompleteField.php replace the suggest method by:

function suggest() {
	$items = array(
		array('value' => 'London'),
		array('value' => 'Paris'),
		array('value' => 'Rome') 
	);
	return json_encode($items);
}

Log in to your admin. Then do a /dev/build/?flush=1

Now if you go to the following url (replace the path to your admin section):

http://www.mydomain/admin/EditForm/field/Test/suggest?term=par

It should return something like:

[{"value":"London"},{"value":"Paris"},{"value":"Rome"}]

Please let me know...

Avatar
BlueScreen

Community Member, 36 Posts

18 March 2011 at 12:04pm

Edited: 18/03/2011 12:58pm

Basic is good for me, I can't deal with anything extremely sophisticated right now. If I can't get autocomplete working soon my boss will start asking me questions. So yes lets take this a step further.

I followed the instructions: modified page.php, logged into the admin, ran dev/build and entered the URL.

The result was a blank page with some cryptic "I can't let you do that, Dave" message on it. My log outputted this: 'Unknown column 'Page.Test' in 'field list' (http://[root]/admin)" so I don't think I did that right. I'll try that again.

meanwhile my previous code in my module has just decided it wants to be broken. Now it tells me this: "TypeError: jQuery(this).autocomplete is not a function".

[EDIT]

I tried again, this time ensuring that I had the updated code from www.balbus.tk and I used a brand new server with nothing else on it.

balbusautocomplete.php

    function setSourceClass($classname) {
		SS_Log::log(new Exception('Autocomplete: setSourceClass('.$className.')'), SS_Log::NOTICE);
        $this->sourceClass = $classname;
    }
 
    /*
     *  set the sourcefield
     */
    function setSourceField($fieldname) {
		SS_Log::log(new Exception('Autocomplete: setSourceField('.$fieldName.')'), SS_Log::NOTICE);
        $this->sourceField = $fieldname;
    }
 
    /*
     *  Override the existing TextField method and add
     *  the jQuery requirements and the new field ettributes:
     *  src          - url called by ajax to retrieve the suggestions
     *  autocomplete - flag setting autocompletion 'on' or 'off'
     */
    function Field() {
 		SS_Log::log(new Exception('Autocomplete: Field()'), SS_Log::NOTICE);
        // Note: sometimes loading new JavaScripts in the CMS
        //       will not work. Looks like the assets/_CombinedFiles
        //       are the culprits (in that case just remove them)
  
        // Get jQuery from SilverStripe thirdparty
        Requirements::javascript(
            THIRDPARTY_DIR . '/jquery/jquery.js'
        );
  
        // Get the UI from Google (the SilverStripe way)
        Requirements::javascript(
            'http://ajax.googleapis.com/ajax/libs/jqueryui'.
            '/1.8.1/jquery-ui.min.js'
        );
        Requirements::css(
            'http://ajax.googleapis.com/ajax/libs/jqueryui'.
            '/1.8.1/themes/base/jquery-ui.css'
        );
  
        // NEW: Since inline JavaScript isn't loaded by jQueryOnDemand,
        //      an external file is needed to set things in motion:
        Requirements::javascript(
            'balbusessentials/javascript/BalbusAutocompleteField.js'
        );
  
  
        // altered 'text' class to 'text AutocompleteField'
        $attributes = array(
            'type' => 'text',
            'class' => 'text AutocompleteField ' . ($this->extraClass()? $this->extraClass(): ''),
            'id' => $this->id(),
            'name' => $this->Name(),
            'value' => $this->Value(),
            'tabindex' => $this->getTabIndex(),
            'maxlength' => ($this->maxLength) ? $this->maxLength : null,
            'size' => ($this->maxLength) ? min( $this->maxLength, 30 ) : null
        );
 
        if($this->disabled) $attributes['disabled'] = 'disabled';
  
        // NEW: add een attribute to enable autocompletion
        $attributes['autocomplete'] = 'on';
 
        // NEW: add a sourceClass/sourcefield, if there is one
        $querystring = http_build_query(array(
            'sclass' => $this->sourceClass,
            'sfield' => $this->sourceField
        ), '', '&');
 
 
        // NEW: add the current link with the Action=suggest
        // parameter added as a new src parameter
        $attributes['src'] =
            parse_url($this->Link(),PHP_URL_PATH) . '/suggest?' . $querystring;
 
        // Create the tag
		SS_Log::log(new Exception('Autocomplete: creating Tag: '.http_build_query($attributes)), SS_Log::NOTICE);
        return $this->createTag('input', $attributes);
    }
  
  		function suggest() { 
			SS_Log::log(new Exception('Autocomplete: suggest()'), SS_Log::NOTICE);
   			$items = array( 
      			array('value' => 'London'), 
      			array('value' => 'Paris'), 
      			array('value' => 'Rome') 
   			); 
			SS_Log::log(new Exception('Autocomplete: suggesting: '.implode($items)), SS_Log::NOTICE);
   			return json_encode($items); 
		}

balbusautocomplete.js

alert("autocomplete running");
try{
	BalbusAutocompleteField = Class.create();
}catch(exception){
	alert("class create failed: "+exception);
}
try{
	BalbusAutocompleteField.applyTo('input.AutocompleteField');
}catch(exception){
	alert("class apply failed: "+exception);
}
BalbusAutocompleteField.prototype = {
	
	// initialize Autocomplete fields
	initialize: function() {
        // only initialize once
        if (!this.strSource){
 			
            // get source string src attribute
            this.strSource = jQuery(this).attr('src');
 			alert("autocomplete source found: "+this.strSource);
			
            // remove src attribute from the textfield
            jQuery(this).attr('src', '');
 			try{
            	// jQuery Autocomplete
            	jQuery(this).autocomplete({
            	    source: this.strSource,
            	    minLength: 2
            	});
			}catch(exception){
				alert("autocomplete failed: "+exception);
			}
			alert("autocomplete end");
        } else {
			alert("source not found");
		}
    }
};

page.php

	public static $db = array(
		'Test' => 'Varchar' 
	);

	public static $has_one = array(
	);

	function getCMSFields() {
   		$fields = parent::getCMSFields();

   		$fields->addFieldsToTab('Root.Content.Autocomplete', array(
      		new BalbusAutocompleteField (
        		$name = 'Test',
         		$title = 'Autocomplete testfield'
      		)	
   		));

   		return $fields;
	}

The link to http://[root]/admin/EditForm/field/Test/suggest?term=par definitely returns the following message:

"I can't handle sub-URLs of a Form object."

Avatar
BlueScreen

Community Member, 36 Posts

18 March 2011 at 2:12pm

new test case: I created two files in my fresh install of silverstripe

mysite/code/Test_Contoller.php

class Test extends Page {
	
}

class Test_Controller extends Page_Controller {
	
	public static $allowed_actions = array(
		'index',
		'suggest',
		'Form'
	);
	
	function init() {
		parent::init();
	}
	
	function index() 
	{
		$this->init();
		return $this->renderWith('testform');
	}
	
	function Form(){
		SS_Log::log(new Exception('Rendering dummy form'), SS_Log::NOTICE);
		$fields = new FieldSet(
      		new BalbusAutocompleteField ( 
      	   		$name = 'Test', 
      	   		$title = 'Autocomplete testfield' 
     	 	) 
	);
		
	    // Create action
	    $actions = new FieldSet(
			new FormAction('dummy', 'Submit')
		);
		
	    $form = new Form($this, 'DummyForm', $fields, $actions);
		return $form;
	}
	
	function link() {
		
	}

mysite/templates/includes/testform.ss

<script language="javascript" type="text/javascript">
//put jquery directly into here to try and force it
</script>
<style>#formbox {width: 600px; padding: 15px; border: solid;}</style>
<div id="formbox">
	<p>Testing</p> 
	$Form
</div>

mysite/_config.php


Director::addRules(100, array(
'test' => 'Test_Controller'
));

error_reporting(E_ALL);
ini_set('display_errors', 1);
SS_Log::add_writer(new SS_LogFileWriter(Director::baseFolder() . '/error_log.txt'), SS_Log::ERR); 
SS_Log::add_writer(new SS_LogFileWriter(Director::baseFolder() . '/error_log.txt'), SS_Log::NOTICE); 
SS_Log::add_writer(new SS_LogFileWriter(Director::baseFolder() . '/error_log.txt'), SS_Log::WARN);
Director::set_environment_type("dev");

navigate to [root]/test to see it work..or not work. The jquery does not appear to load with this configuration even though it closely imitates how my custom module uses forms. If I force the jquery in, it crashes on the first line.

The php seems to be working, it usually gets about as far as the Field function:

[18-Mar-2011 01:54:17] Notice at \mysite\code\Test_Controller.php line 29: Rendering dummy form
[18-Mar-2011 01:54:17] Notice at \mysite\code\BalbusAutocompleteField.php line 44: Autocomplete: Field() 
[18-Mar-2011 01:54:17] Notice at \mysite\code\BalbusAutocompleteField.php line 101: Autocomplete: creating Tag: type=text&amp;class=text+AutocompleteField+&amp;id=Form_DummyForm_Test&amp;name=Test&amp;value=&amp;autocomplete=on&amp;src=DummyForm%2Ffield%2FTest%2Fsuggest%3Fsclass%3D%26sfield%3D

Try it out, might help you find out why sometime it works and sometimes it does not.

Avatar
martimiz

Forum Moderator, 1391 Posts

19 March 2011 at 12:03am

OK - this is getting a bit complex... Again, I worked on the thing :-( , and added an alternative fully jQuery BalbusAutocomplete.js javascript, that does support the field in the frontend - at least for me (bottom of the source page).

Please check out this example, and let me know if it works for you (type auto into the field)
http://www.balbus.tk/autocompletefield-example/

If you still can't get yours to work, I'll see if I can put together a small module for you to download and install. Are you looking to use the field in your CMS or frontend?

Avatar
BlueScreen

Community Member, 36 Posts

19 March 2011 at 1:02am

Edited: 19/03/2011 1:09am

FrontEnd only

I will be coding in the autocompletefields exactly how I did in my test case: seperate pages and controllers physically located in mysite or another module

The example on your site seems to work perfectly, its just what I need! :D Now if only I could figure out why my own module doesn't like it.

If you can package the source code of that into a separate module, I'll run it on a fresh blank copy of sapphire on my home PC. If it works on the webserver I'm running at home and not at the one at work I can narrow down the problem to just conflicts or dependencies at my work webserver.