昨天,我们实现了一个非常强大的搜索引擎,感谢Zend Lucene库。
今天,我们要利用AJAX给搜索引擎增加实时响应 功能,加强搜索引擎的响应能力。
使用AJAX时,我们希望无论用户的浏览器是否支持JavaScript,搜索引擎都可以正常工作。 所有我们决定使用非侵入式 JavaScript 实现这个新功能。非侵入式JavaScript同时也可以很好的实现, CSS和JavaScript行为 与客户端HTML代码中分类。
安装jQuery
为了避免重复开发并做到浏览器兼容,我们将使用现有的JavaScript库——jQuery。 Symfony框架能与任何JavaScript库一起工作。
到 jQuery 网站,下载最新版本,将.js问放到web/js/
目录中。
tip
也可以使用sfJqueryReloadedPlugin 插件安装jQuery。
加载jQuery
因为所有页面都需要jQuery,所以我们将它放到layout的<head>
标签中。注意,
使用include_javascripts()
调用前,先插入use_javascript()
函数:
<!-- apps/frontend/templates/layout.php --> <?php use_javascript('jquery-1.2.6.min.js') ?> <?php include_javascripts() ?> </head>
我们可以直接用<script>
标签调用jQuery,但使用use_javascript()
辅助函数可以
保证同一个JavaScript文件不会被调用两次。
note
For
performance reasons,
you might also want to move the include_javascripts()
helper call just
before the ending </body>
tag.
TIP
如果使用sfJqueryReloadedPlugin插件,在模板中使用use_helper(jQuery
)调用jQuery。
Adding Behaviors
要实现实时搜索(live search),意味着每当用户在搜索框中有一个键入动作,程序 就要访问一次服务器;并将返回的内容更新到页面的选区,而不用刷新整个页面。
我们不需要使用on*()
添加行为,jQuery可以在页面完全载入时,直接将行为
添加到DOM元素中。这样一来,如果浏览器不支持JavaScript,行为将不被添加,
搜索引擎还还会象以前那样工作。
首先,拦截用户在搜索框中的键入动作:
$('#search_keywords').keyup(function(key) { if (this.value.length >= 3 || this.value == '') { // do something } });
note
现在还不要添加代码,因为还要有做很多修改。最终的JavaScript代码,会在下一节添加到layout中。
每次键入内容,jQuery都会执行一次上面定义的匿名函数,但前提是用户键入内容超过3个 字符或input标签中内容被清空时。
要实现AJAX调用,只需在DOM元素上使用load()
方法:
$('#search_keywords').keyup(function(key) { if (this.value.length >= 3 || this.value == '') { $('#jobs').load( $(this).parents('form').attr('action'), { query: this.value + '*' } ); } });
因为AJAX调用和“正常”调用使用同一个动作,所以我们下面将对动作进行修改。
现在先来调整一下搜索按钮。当浏览器支持JavaScript时,让它隐藏起来:
$('.search input[type="submit"]').hide();
User Feedback
我们知道,从AJAX请求服务器到服务器返回响应(response)需要一段时间,所以页面内容都不会 马上更新。这段等待时间,你应该向用户显示一些提示信息(visual feedback),告诉用户请求正在执行。
常用的方法是在这段时间里,显示一个载入图标。下面我们在layout中加入载入图标,并在 不需要的时候隐藏该图标:
<!-- apps/frontend/templates/layout.php --> <div class="search"> <h2>Ask for a job</h2> <form action="<?php echo url_for('@job_search') ?>" method="get"> <input type="text" name="query" value="<?php echo $sf_request->getParameter('query') ?>" id="search_keywords" /> <input type="submit" value="search" /> <img id="loader" src="/legacy/images/loader.gif" style="vertical-align: middle; display: none" /> <div class="help"> Enter some keywords (city, country, position, ...) </div> </form> </div>
note
这个载入图标是专门为Jobeet设计的。你可以创建自己的图标,也有许多提供免费图标的网站,如 http://www.ajaxload.info/.
现在各部分都已经准备好了,创建一个search.js
包含下面的JavaScript代码:
// web/js/search.js $(document).ready(function() { $('.search input[type="submit"]').hide(); $('#search_keywords').keyup(function(key) { if (this.value.length >= 3 || this.value == '') { $('#loader').show(); $('#jobs').load( $(this).parents('form').attr('action'), { query: this.value + '*' }, function() { $('#loader').hide(); } ); } }); });
你同样需要更新layout,引用这个新文件:
<!-- apps/frontend/templates/layout.php --> <?php use_javascript('search.js') ?>
AJAX in an Action
如果浏览器支持JavaScript,jQuery将拦截搜索框的键盘输入,并调用search
动作。如果
不支持,用户点击搜索按钮,程序也会调用这个search
动作。
两种方式请求同一个动作,但需要返回的内容却不同,所以我们在search
动作中使用
isXmlHttpRequest()
进行判断。当AJAX调用时,isXmlHttpRequest()
方法会返回true
。
note
isXmlHttpRequest()
方法支持Prototype, Mootools和 jQuery这些主流JavaScript库。
// apps/frontend/modules/job/actions/actions.class.php public function executeSearch(sfWebRequest $request) { if (!$query = $request->getParameter('query')) { return $this->forward('job', 'index'); } $this->jobs = Doctrine::getTable('JobeetJob')->getForLuceneQuery($query); if ($request->isXmlHttpRequest()) { return $this->renderPartial('job/list', array('jobs' => $this->jobs)); } }
jQuery只更新#jobs
DOM元素内容,不重新加载整个页面,页面不需要layout装饰。因此,
一般情况下AJAX请求动作时layout默认是关闭的。
此外,我们只需要返回job/list
局部模板的内容,而不用返回整个模板。
renderPartial()
方法将局部模板作为响应返回给客户端。
当用户清空搜索框,或没有匹配的结果时,我们需要显示一条提示信息。我们使用
renderText()
方法设置一个简单的测试字符串:
// apps/frontend/modules/job/actions/actions.class.php public function executeSearch(sfWebRequest $request) { if (!$query = $request->getParameter('query')) { return $this->forward('job', 'index'); } $this->jobs = Doctrine::getTable('JobeetJob')->getForLuceneQuery($query); if ($request->isXmlHttpRequest()) { if ('*' == $query || !$this->jobs) { return $this->renderText('No results.'); } else { return $this->renderPartial('job/list', array('jobs' => $this->jobs)); } } }
tip
你也可以通过renderComponent()
方法返回组件。
测试AJAX
因为Symfony浏览程序不能模拟JavaScript,你需要使用其它方法来测试AJAX。这意味着, 你需要手动添加jQuery或其它主流JavaScript库的请求头信息:
// test/functional/frontend/jobActionsTest.php $browser->setHttpHeader('X_REQUESTED_WITH', 'XMLHttpRequest'); $browser-> info('5 - Live search')-> get('/search?query=sens*')-> with('response')->begin()-> checkElement('table tr', 2)-> end() ;
setHttpHeader()
方法为浏览器下一次请求(HTTP header)设置一个HTTP头。
明天见
昨天,我们使用Zend Lucene库实现了搜索引擎。今天,我们用jQuery提高了它的 响应能力。Symfony框架提供了所有轻松构建MVC程序的工具,也可以与其它组件 协调工作。总之,使用最好的工具,来完成这个工作。
明天,我们学习如果国际化Jobeet网站。
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.