app:search
  • Required Attributes (3)
  • Optional Attributes (15)
  • Examples (1)
  • Source code
Name Type Description Default value
request Appcelerator Message Send Request message being sent to search Not specified
response Appcelerator Message Send Response message for search results Not specified
selected Appcelerator Message Send Message sent when the user has selected an option Not specified
Back to menuExample: Simple Example

This is a simple example that uses the <app:search>.

Here is a real simple example

$MQ('l:search.response', {result: ['item 1','item 2','item 3']});
<app:search request="l:search.request" response="l:search.response" selected="l:search.selected" key="search" property="result">
</app:search>
<app:script on="l:search.request then execute">
	$MQ('l:search.response', {result: ['item 1','item 2','item 3']});
</app:script>

Here’s another example with a complex search result.

#{name} #{city}, #{job} $MQ('l:search2.response', {result: [{id: 1, name: 'John', city: 'Atlanta', job: 'Engineer'}, {id: 2, name: 'Bob', city: 'New York', job: 'Mayor'}, {id: 3, name: 'Bill', city: 'Seattle', job: 'Plumber'}]});
You've selected an item with id .
<app:search request="l:search2.request" response="l:search2.response" selected="l:search2.selected" key="search" property="result">
	<html:div>
		#{name}
	</html:div>
	<html:div>
		#{city}, #{job}
	</html:div>
</app:search>
<app:script on="l:search2.request then execute">
	$MQ('l:search2.response', {result: [{id: 1, name: 'John', city: 'Atlanta', job: 'Engineer'}, {id: 2, name: 'Bob', city: 'New York', job: 'Mayor'}, {id: 3, name: 'Bill', city: 'Seattle', job: 'Plumber'}]});
</app:script>
<div style="border:1px solid #ccc;background-color:#f6f6f6;padding:10px;margin-top:10px;display:none" on="l:search2.selected then effect[appear] or l:search2.request then effect[fade]">
	You've selected an item with id <span on="l:search2.selected then value[value]"></span>.
</div>
  1 /*
  2  * This file is part of Appcelerator.
  3  *
  4  * Copyright (C) 2006-2008 by Appcelerator, Inc. All Rights Reserved.
  5  * For more information, please visit http://www.appcelerator.org
  6  *
  7  * Appcelerator is free software: you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License as published by
  9  * the Free Software Foundation, either version 3 of the License, or
 10  * (at your option) any later version.
 11  *
 12  * This program is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  * GNU General Public License for more details.
 16  * 
 17  * You should have received a copy of the GNU General Public License
 18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19  *
 20  */
 21 
 22 Appcelerator.Widget.Search =
 23 {
 24 	getName: function()
 25 	{
 26 		return 'appcelerator search';
 27 	},
 28 	getDescription: function()
 29 	{
 30 		return 'search widget';
 31 	},
 32 	getVersion: function()
 33 	{
 34 		return '1.0.1';
 35 	},
 36 	getSpecVersion: function()
 37 	{
 38 		return 1.0;
 39 	},
 40 	getAuthor: function()
 41 	{
 42 		return 'Hamed Hashemi';
 43 	},
 44 	getModuleURL: function ()
 45 	{
 46 		return 'http://www.appcelerator.org';
 47 	},
 48 	isWidget: function ()
 49 	{
 50 		return true;
 51 	},
 52 	getWidgetName: function()
 53 	{
 54 		return 'app:search';
 55 	},
 56 	getAttributes: function()
 57 	{
 58         var T = Appcelerator.Types;        
 59         return [{
 60 			name: 'request',
 61 			optional: false,
 62 			type: T.messageSend,
 63 			description: "Request message being sent to search"
 64         }, {
 65 			name: 'response',
 66 			optional: false,
 67 			type: T.messageSend,
 68 			description: "Response message for search results"
 69         }, {
 70 			name: 'selected',
 71 			optional: false,
 72 			type: T.messageSend,
 73 			description: "Message sent when the user has selected an option"
 74         }, {
 75 			name: 'key',
 76 			optional: true,
 77 			defaultValue: 'key',
 78 			type: T.identifier,
 79 			description: "Parameter name used in the request for the query"
 80         }, {
 81 			name: 'property',
 82 			optional: true,
 83 			defaultValue: 'result',
 84 			type: T.identifier,
 85 			description: "Property in the response used for the results"
 86         }, {
 87 			name: 'resultId',
 88 			optional: true,
 89 			defaultValue: 'id',
 90 			type: T.identifier,
 91 			description: "Property to use from the result to send selected message when using complex results"
 92         }, {
 93 			name: 'inputWidth',
 94 			optional: true,
 95 			defaultValue: '200',
 96 			type: T.cssDimension,
 97 			description: "Width of the input field"
 98         }, {
 99 			name: 'resultWidth',
100 			optional: true,
101 			defaultValue: '220',
102 			type: T.cssDimension,
103 			description: "Width of the results"
104         }, {
105 			name: 'delay',
106 			optional: true,
107 			defaultValue: 200,
108 			type: T.time,
109 			description: "Delay before firing request message"
110         }, {
111 			name: 'indicator',
112 			optional: true,
113 			type: T.elementId,
114 			description: "Indicator id to show or hide"
115         }, {
116 			name: 'activeClass',
117 			optional: true,
118 			defaultValue: 'search_result_active',
119 			type: T.cssClass,
120 			description: "Active class for selecting search results"
121         }, {
122 			name: 'inactiveClass',
123 			optional: true,
124 			defaultValue: 'search_result_inactive',
125 			type: T.cssClass,
126 			description: "Inactive class for selecting search results"
127         }, {
128 			name: 'fieldset',
129 			optional: true,
130 			type: T.fieldset,
131 			description: "Fieldset applied to the input element"
132         }, {
133 			name: 'class',
134 			optional: true,
135 			type: T.cssClass,
136 			description: "Class applied to the input element"
137         }, {
138 			name: 'name',
139 			optional: true,
140 			type: T.identifier,
141 			description: "Name applied to the input element"
142         }, {
143 			 name: 'value',
144 			 optional: true,
145 			 description: "Value attribute applied to the input element"
146    		}, {
147 			name: 'on',
148 			optional: true,
149 			type: T.onExpr,
150 			description: "On attribute applied to the input element"
151 		}, {
152 			name: 'safariSearch',
153 			defaultValue: true,
154 			optional: true,
155 			type: T.bool,
156 			description: "Whether or not to use type search for Safari"
157 	    }];
158 	},
159 	ignoreFieldset: function()
160 	{
161 	    return true;
162 	},
163 	compileWidget: function(params)
164 	{
165 		var id = params['id'];
166 		var input = $(id);
167 		var request = params['request'];
168 		var response = params['response'];
169 		var selected = params['selected'];
170 		var elementScope = params['scope'];	
171 		var key = params['key'];
172 		var property = params['property'];
173 		var activeClass = params['activeClass'];
174 		var inactiveClass = params['inactiveClass'];
175 		var select = $(id+'_select');
176 		var resultId = params['resultId'];
177 		var delay = params['delay'];
178 		var indicator = params['indicator'];
179 		var hideResults = params['hideResults'];
180 		var compiled = null;
181 
182 		if (params['template'])
183 		{
184 			compiled = eval(params['template'] + '; init_'+id);
185 		}
186 
187         $MQL(response,
188 		function(t, data, datatype, direction)
189 		{
190 			if (indicator)
191 			{
192 				Element.hide(indicator);
193 			}
194 			var value = property ? Object.getNestedProperty(data, property) : data;
195 
196 			Appcelerator.Compiler.destroy(select);		
197 
198 			while (select.hasChildNodes())
199 			{
200 				select.removeChild(select.firstChild);
201 			}
202 
203 			var selectableData = [];
204 
205 			for (var i = 0; i < value.length; i++)
206 			{
207 				(function(){
208 					var optionId = Appcelerator.Compiler.generateId();
209 					var optionValue = value[i];
210 					var optionCode = '<div id="'+optionId+'" style="width: 100%" class="search_result_inactive">';
211 					if (compiled)
212 					{
213 						for (idx in optionValue)
214 						{
215 							if (typeof optionValue[idx] == 'string')
216 							{
217 								optionValue[idx] = optionValue[idx].replace(/'/,'\u2019');
218 							}
219 						}
220 						optionCode += compiled(optionValue);							
221 					}
222 					else
223 					{
224 						optionCode += optionValue;
225 					}
226 					optionCode += '</div>';
227 					new Insertion.Bottom(select, optionCode);
228 					var option = $(optionId);
229 					option.onclick = function(event)
230 					{
231 						if (compiled)
232 						{
233 							$MQ(selected, {value: optionValue[resultId]});
234 						}
235 						else
236 						{
237 							$MQ(selected, {value: optionValue});
238 							input.value = optionValue;
239 						}
240 					}
241 					option.onmouseover = function()
242 					{
243 						for (var i = 0; i < select.selectableData.length; i++)
244 						{
245 							Element.addClassName($(select.selectableData[i].id), inactiveClass);
246 							Element.removeClassName($(select.selectableData[i].id), activeClass);							
247 						}
248 						Element.removeClassName(this, inactiveClass);
249 						Element.addClassName(this, activeClass);
250 					};
251 					option.onmouseout = function()
252 					{
253 						Element.addClassName(this, inactiveClass);
254 						Element.removeClassName(this, activeClass);							
255 					};
256 					selectableData.push({id: optionId, value: optionValue});
257 				})();
258 			}
259 
260 			select.selectedIndex = -1;
261 			select.selectableData = selectableData;
262 
263 			Appcelerator.Compiler.dynamicCompile(select);
264 
265 			if (select.selectableData.length > 0)
266 			{
267 				Effect.Appear(id+'_results', {duration: 0.5});
268 			}
269 			else
270 			{
271 				Element.hide(id+'_results');
272 			}
273 		},
274 		elementScope);
275 
276 		var selectFunction = function()
277 		{
278 			(function(){
279 				if (select.selectedIndex >= 0)
280 				{
281 					var selected = select.selectableData[select.selectedIndex];
282 					if (selected)
283 					{
284 						if (!compiled)
285 						{
286 							input.value = selected.value;
287 						}
288 
289 						for (var i = 0; i < select.selectableData.length; i++)
290 						{
291 							Element.addClassName($(select.selectableData[i].id), inactiveClass);
292 							Element.removeClassName($(select.selectableData[i].id), activeClass);							
293 						}
294 						Element.removeClassName($(selected.id), inactiveClass);							
295 						Element.addClassName($(selected.id), activeClass);
296 					}
297 				}
298 				else
299 				{
300 					for (var i = 0; i < select.selectableData.length; i++)
301 					{
302 						Element.addClassName($(select.selectableData[i].id), inactiveClass);
303 						Element.removeClassName($(select.selectableData[i].id), activeClass);
304 					}
305 				}
306 			})();
307 		};
308 
309 		var timer = null;
310 		var keystrokeCount = 0;
311 		var timerFunc = function()
312 		{
313 			if (indicator)
314 			{
315 				Element.show(indicator);
316 			}
317 
318 			keystrokeCount = 0;
319 			var payload = {};
320 			payload[key] = input.value; 
321 			$MQ(request, payload);
322 		};
323 
324 		input.onkeydown = function (event)
325 		{
326 			(function(){
327 				event = event || window.event;
328 
329 				switch(event.keyCode) 
330 				{
331 					case Event.KEY_TAB:
332 					case Event.KEY_LEFT:
333 					case Event.KEY_RIGHT:
334 					case Event.KEY_ESC:
335 					{
336 						Effect.Fade(id+'_results', {duration: 0.5});
337 						Event.stop(event);
338 						return;
339 					}
340 					case Event.KEY_RETURN:
341 					{
342 						Effect.Fade(id+'_results', {duration: 0.5});
343 						if (select.selectableData && select.selectedIndex >= 0)
344 						{
345 							if (compiled)
346 							{
347 								$MQ(selected, {value: select.selectableData[select.selectedIndex].value[resultId]});
348 							}
349 							else
350 							{
351 								$MQ(selected, {value: select.selectableData[select.selectedIndex].value});
352 							}
353 						}
354 						Event.stop(event);
355 						return;
356 					}
357 					case Event.KEY_UP:
358 					{
359 						if (select.selectableData && select.selectableData.length > 0)
360 						{
361 							Effect.Appear(id+'_results', {duration: 0.5});
362 							select.selectedIndex--;
363 							if (select.selectedIndex < 0)
364 							{
365 								select.selectedIndex = select.selectableData.length-1;
366 							}
367 							selectFunction();
368 						}
369 						Event.stop(event);
370 						return;
371 					}
372 					case Event.KEY_DOWN:
373 					{
374 						if (select.selectableData && select.selectableData.length > 0)
375 						{
376 							Effect.Appear(id+'_results', {duration: 0.5});
377 							select.selectedIndex++;
378 							if (select.selectedIndex > select.selectableData.length-1)
379 							{
380 								select.selectedIndex = 0;
381 							}
382 							selectFunction();
383 						}
384 						Event.stop(event);
385 						return;
386 					}
387 				}
388 
389 				if (timer)
390 				{
391 					clearTimeout(timer);
392 					timer = null;
393 				}
394 
395 				if (keystrokeCount++ < 10)
396 				{
397 					timer = setTimeout(timerFunc, delay);
398 				}
399 				else
400 				{
401 					timerFunc();
402 				}
403 			})();
404 		};
405 
406 		input.onblur = function (event)
407 		{
408 			setTimeout(function()
409 			{
410 				Effect.Fade(id+'_results', {duration: 0.5});
411 			},100);
412 		}
413 
414 		input.onfocus = function (event)
415 		{
416 			if (input.value.length > 0 && select.selectableData && select.selectableData.length > 0)
417 			{
418 				Effect.Appear(id+'_results', {duration: 0.5});
419 			}
420 		}
421 	},
422 	buildWidget: function(element,parameters)
423 	{
424 		parameters['scope'] = element.scope;
425 
426 		if (element.innerHTML.strip().length > 0)
427 		{
428 			parameters['template'] = Appcelerator.Compiler.compileTemplate(Appcelerator.Compiler.getHtml(element),true,'init_'+element.id);		
429 		}
430 		
431 		var inputType = (Appcelerator.Browser.isSafari && (parameters['safariSearch'] != "false")) ? 'search' : 'text';
432 
433 		var html = '<div style="position: relative">';
434 		html += '<table style="padding: 0; margin: 0" cellpadding="0" cellspacing="0"><tr><td><input autocomplete="off" type="'+inputType+'" id="'+element.id+'" style="width: '+parameters['inputWidth']+'px" ';
435 		if (parameters['fieldset'])
436 		{
437 			html += ' fieldset="'+parameters['fieldset']+'" ';
438 		}
439 		if (parameters['class'])
440 		{
441 			html += ' class="'+parameters['class']+'" ';
442 		}
443 		if (parameters['name'])
444 		{
445 			html += ' name="'+parameters['name']+'" ';
446 		}
447 		html += '/></td></tr></table>';
448 		html += '<div style="display:none;z-index:2;position:absolute; left: 0px" id="'+element.id+'_results" on="'+parameters['selected']+' then hide">';
449 		html += '<div id="'+element.id+'_select" style="width: '+parameters['resultWidth']+'px; border: 1px #000 solid; cursor: pointer;"></div></div>';
450 		html += '</div>';
451 
452 		return {
453 			'position' : Appcelerator.Compiler.POSITION_REPLACE,
454 			'presentation' : html,
455 			'wire' : true,
456 			'compile' : true
457 	   };
458 	}
459 };
460 
461 Appcelerator.Core.loadModuleCSS('app:search','search.css');
462 Appcelerator.Widget.register('app:search',Appcelerator.Widget.Search);
463