发新话题
打印

[symfony]高度交互性的应用 - 集成Ajax

根据响应状态更新确定的元素

如果远程动作失败了, 远程 辅助函数不是更新有成功的响应更新的元素, 而是可以选择更新另一个元素.为了达到这个目的, 只要将update参数的值分开存放进一个数组中,并且为success和failure两种情况设定要更新的不同的元素值即可. 这是很有用的一个功能, 例如, 在一个页面中可以有许多Ajax交互和一个错误反馈区. 表11-16显示了这种有条件更新的用法.

表11-16 处理一个有条件更新.
[php]
<div id="error"></div>
<div id="feedback"></div>
<p>Hello, World!</p>
<?php echo link_to_remote('Delete this post', array(
    'update'   => array('success' => 'feedback', 'failure' => 'error')
    'url'      => 'post/delete?id='.$post->getId(),
)) ?>

TIP Only HTTP error codes (500, 404, and all codes not in the 2XX range) will trigger the failure update, not the actions returning sfView::ERROR. So if you want to make an action return an Ajax failure, it must call $this->getResponse()->setStatusCode(404) or similar.HTTP错误代码(500,400 及所有不在2XX范围内的代码)只触发失败更新, 而不是返回sfView::ERROR的动作. 所以如果你想写一个返回Ajax失败的动作, 你必须调用类似$this->getResponse()->setStatusCode(404)的函数.

TOP

根据元素位置更新元素

通过加入一个position参数, 你可以对相对于某个具体元素的元素进行更新,就象update_element_function()辅助函数 一样. 表11-17是一个示例.

表11-17 用位置参数改变响应位置.
[php]
<div id="feedback"></div>
<p>Hello, World!</p>
<?php echo link_to_remote('Delete this post', array(
    'update'   => 'feedback',
    'url'      => 'post/delete?id='.$post->getId(),
    'position' => 'after',
)) ?>

这个例子将在feedback元素之后插入Ajax调用的响应, 也就是说, 在

<

div>和

<

p>之间. 有了这个方法, 你可以调用多个Ajax调用, 并在update元素后看到响应是如何聚集的.

position 参数可以取以下值:
before: 在元素之前
after: 在元素之后
top: 在元素的内容顶部
bottom: 在元素的内容底部

TOP

根据条件更新元素

远程调用可以加入另一个参数, 以便在真正发出XMLHttpRequest之前用户可以确认.如表11-18所示:

表11-18在远程函数中加入确认参数以便在调用之前取得确认.
[php]
<div id="feedback"></div>
<?php echo link_to_remote('Delete this post', array(
    'update'   => 'feedback',
    'url'      => 'post/delete?id='.$post->getId(),
    'confirm'  => 'Are you sure?',
)) ?>

这样, 当用户点击了链接后, 就会弹出一个显示了”Are you sure?”的javascript 对话框,只有当用户点击OK表示确认后,post/delete动作才会被调用.

如果你提供了一个condition参数, 远程调用也可以根据在浏览器端执行的一个测试来确定是否被执行, 表11-19给出一个例子:

表11-19 根据客户端的测试来确定是否调用远程函数.
[php]
<div id="feedback"></div>
<?php echo link_to_remote('Delete this post', array(
    'update'    => 'feedback',
    'url'       => 'post/delete?id='.$post->getId(),
    'condition' => "$('elementID') == true",
)) ?>

TOP

确定Ajax 请求方法

Ajax请求默认采用 POST方法. 如果你调用一个并不改变数据的Ajax函数,或者, 你想用一个含有内置验证方法的表单作为Ajax调用的结果, 你可能想将Ajax请求方法改为GET. 表11-20显示了用method选项改变Ajax请求方法的例子.

表11-20 改变Ajax的请求方法
[php]
<div id="feedback"></div>
<?php echo link_to_remote('Delete this post', array(
    'update'    => 'feedback',
    'url'       => 'post/delete?id='.$post->getId(),
    'method'    => 'get',
)) ?>

TOP

授权脚本运行

如果一个Ajax调用函数的响应代码(即插入在update元素中的由服务器发出的代码)包含Javascript, 你可能会奇怪在默认情况下这些代码并不执行. 这是为了减少远程攻击的风险并且让开发者可以在确认了响应中的代码后才执行脚本.

这就需要你用script选项来明确声明可以执行远程响应中的脚本. 表11-21的Ajax调用指明了可以执行从远程响应中得到的Javascript.

表11-21 授权执行Ajax响应中的脚本.
[php]
<div id="feedback"></div>
// If the response of the post/delete action contains JavaScript,
// allow it to be executed by the browser
<?php echo link_to_remote('Delete this post', array(
    'update' => 'feedback',
    'url'    => 'post/delete?id='.$post->getId(),
    'script' => true,
)) ?>

如果远程模板包含象remote_function()之类的Ajax 辅助函数, 要记得这些PHP函数生成的是Javascript 代码, 除非你加上'script' => true选项, 否则它们不会执行.

NOTE Even if you enable script execution for the remote response, you won't actually see the scripts in the remote code, if you use a tool to check the generated code. The scripts will execute but will not appear in the code. Although peculiar, this behavior is perfectly normal.注意: 对于远程响应, 即使你允许执行脚本, 如果你想用某种工具去检查生成的代码, 你仍将看不到远程代码中的脚本. 这些脚本只会执行却不会出现在代码中. 虽然看起来很奇怪, 这种方式却是很正常的.

TOP

创建回调函数

Ajax交互的一个重要不足就是在要更新的区域更新之前, 用户看不见这种交互. 这意味着,如果网络很慢或服务器运行失败, 虽然它们的动作根本就没有执行, 用户仍以为它们正在处理中. 所以将Ajax交互中发生事件传达给用户是非常重要的.

每个远程请求都被默认为一个异步过程, 在这个过程中, 可以触发各种javascript回调函数, 诸如进度条之类. 所有的回调函数都可以访问request对象, 该对象保存着隐式的XMLHttpRequest. 回调函数对应于Ajax交互中发生的以下事件:
before: 在初始化请求之前
after: 在初始化请求之后并在导入之前
loading: 当远程响应正被浏览器导入时
loaded: 当远程响应以被浏览器导入完成时
interactive: 当用户可以和远程响应交互时, 即使该响应还没有完全导入
success: 当XMLHttpRequest已完成, 而且HTTP状态码在2XX的范围内
failure: 当XMLHttpRequest已完成, 而且HTTP状态码不在2XX的范围内
404: 当请求返回404状态时
complete: 当XMLHttpRequest完成时.(如果存在的话, 将会在success或failure之后触发).

例如, 我们常会在初始化远程调用时显示一个导入进度条, 而在收到响应后隐去进度条. 要达到这个目的, 只要如表11-22那样, 在Ajax调用中简单地加入loading 和complete参数即可.

表11-22 用Ajax回调函数显示和隐藏一个活动进度条.
[php]
<div id="feedback"></div>
<div id="indicator">Loading...</div>
<?php echo link_to_remote('Delete this post', array(
    'update'   => 'feedback',
    'url'      => 'post/delete?id='.$post->getId(),
    'loading'  => "Element.show('indicator')",
    'complete' => "Element.hide('indicator')",
)) ?>

Show 和hide 方法以及Javascrip Element对象都是Prototype中有用的工具.

TOP

Creating Visual Effects 创建可视的效果

Symfony中集成了视觉效果库script.aculo.us, 这个库可以帮助你完成不仅仅是在页面中显示和隐藏<div> 元素的功能. 在http://script.aculo.us/中可以找到合适的文档资料. 简单地讲, 这个库提供了许多为了获得复杂视觉效果而操纵DOM的Javascript对象和功能.表11-23中列出了一些示例. 因为结果是web页中某个区域的视觉模拟, 所以建议你测试它们的效果以理解它们到底是如何运行的. Script.aculo.us网站提供了动态效果的汇总, 你可以从中得到启发.

表11-23 在Javascript中用script.aculo.us实现视觉效果.
[php]
// 加亮my_field元素
Effect.Highlight('my_field', { startcolor:'#ff99ff', endcolor:'#999999' })

// 为元素添加下拉百叶窗效果
Effect.BlindDown('id_of_element');

// 为元素添加渐隐效果
Effect.Fade('id_of_element', { transition: Effect.Transitions.wobble })

Symfony用visual_effect() 辅助函数封装了Javascript的Effect对象, 这个辅助函数是Javascript 辅助函数组的成员. 它可以输出在通常的链接中用到的Javascript, 如表11-24所示.

表11-24 用visual_effect() 辅助函数在模板中实现视觉效果.
[php]
<div id="secret_div" style="display:none">I was here all along!</div>
<?php echo link_to_function(
  'Show the secret div',
  visual_effect('appear', 'secret_div')
) ?>
// Will make a call to Effect.Appear('secret_div')

visual_effects()辅助函数还可以用在Ajax回调函数中, 表11-25实现了一个类似表11-22的活动进度条, 但是视觉感受更舒适. 当Ajax调用启动, indicator元素就渐渐显现出来, 而当响应到达时, indicator元素又逐步隐去. 另外, 为了吸引用户对feedback元素的注意力, 在该元素被远程调用更新后会被加亮显示.

表11-25 用Ajax 回调函数实现视觉效果.
[php]
<div id="feedback"></div>
<div id="indicator" style="display: none">Loading...</div>
<?php echo link_to_remote('Delete this post', array(
    'update'   => 'feedback',
    'url'      => 'post/delete?id='.$post->getId(),
    'loading'  => visual_effect('appear', 'indicator'),
    'complete' => visual_effect('fade', 'indicator').
                  visual_effect('highlight', 'feedback'),
)) ?>

注意观察是如何在回调函数中通过链接方式将各种视觉效果混合起来的.

TOP

JSON

Javascript对象表示模型(JSON)是一个轻量级的数据交换格式. 简单地讲, 它就是用于传送对象信息的Javascript数组, 如表11-26中的例子所示. 但是JSON对Ajax交互来说有两个主要的好处, 一个是用Javascript易于读取, 二是它可以减小web响应的数据量.

表11-26 Javascript中的JSON对象.
var myJsonData = {"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}

如果一个Ajax动作需要返回结构化的数据给调用页面, 以便javascript进一步处理的话, JSON是一个适合作为响应的格式. 比如说, 如果一个Ajax调用想更新调用者页面的多个元素是, 这非常有用.

例如, 设想有一个象表11-27那样的调用页面, 其中含有两个需要更新的元素. 一个远程辅助函数只能更新页面中的一个元素(要么是title, 要么是name), 而不能同时更新两个.

表11-27 用于多个Ajax更新的模板示例
[php]
<h1 id="title">Basic letter</h1>
<p>Dear <span id="name">name_here</span>,</p>
<p>Your e-mail was received and will be answered shortly.</p>
<p>Sincerely,</p>

要更新两者, 把Ajax响应想象成一个包括以下数组的JSON头:
[["title", "My basic letter"], ["name", "Mr Brown"]]

远程调用就能轻易地解析这个响应并且稍稍利用Javascript就可以更新一行中的几个域. 为达到这个效果,将表11-28中的代码加进表11-27的模板即可.

表11-28 从一个远程响应更新多个元素
[php]
<?php echo link_to_remote('Refresh the letter', array(
  'url'      => 'publishing/refresh',
  'complete' => 'updateJSON(request, json)'
)) ?>

<?php echo javascript_tag("
function updateJSON(request, json)
{
  var nbElementsInResponse = json.length;
  for (var i = 0; i < nbElementsInResponse; i++)
  {
     Element.update(json[i][0], json[i][1]);
  }
}
") ?>

complete 回调函数可以访问响应的json 头并把它传递给第三方函数. 这个updateJSON()函数对JSON头进行迭代, 对数组的每个成员, 用第二个参数更新名字为第一个参数的元素.表11-29 显示了publishing/refresh动作如何返回一个JSON响应.

表11-29 返回JSON头的动作示例
[php]
class publishingActions extends sfActions
{
  public function executeRefresh()
  {
    $output = '[["title", "My basic letter"], ["name", "Mr Brown"]]';
    $this->getResponse()->setHttpHeader("X-JSON", '('.$output.')');

    return sfView::HEADER_ONLY;
  }

HTTP协议允许在响应头中存放JSON.因为响应不包含任何内容,动作只是立即将它作为一个头发送出去.这样它就完全跳过了视图层, 不仅和->renderText()一样快,仅而且更小.

CAUTION There is a severe limitation to the approach shown in Listing 11-29: the maximum size of HTTP headers.There is no official limitation, but large headers may not be well transferred or interpreted by a browser. This means that if your JSON array is large, the remote action should return a normal response, with the JSON as a JavaScript array.注意, 表11-29中的方法有一个服务器的限制, 也即HTTP头的最大容量. 虽然没有正式的规定, 但浏览器不能很好地转换和解释大的HTTP头. 也就是说, 如果你的JSON数组比较大, 那么远程动作会以一个javascript 数组作为JSON来返回一个规范的响应.

JSON已经成为web应用的一个标准. Web服务经常建议用JSON而不是XML去响应, 以便将服务集成到客户端(mashup)而不是服务器端. 所以如果你考虑用哪种格式在服务器和javascript函数间通信, JSON也许是最好的选择.

TIP Since version 5.2, PHP offers two functions, json_encode() and json_decode(), that allow you to convert an array between the PHP syntax and the JSON syntax, and vice versa (http://www.php.net/manual/en/ref.json.php). These facilitate the integration of JSON arrays and Ajax in general.从php5.2开始, php提供了json_encode()和json_decode()两个函数, 以便你在PHP语法和JSON语法之间进行数组转换. 这有助于JSON数组和Ajax的集成.可以参考(http://www.php.net/manual/en/ref.json.php).

TOP

用Ajax完成复杂的交互

在symfony的Ajax 辅助函数里, 你会发现有些工具只需一个简单的调用就可以完成复杂的交互. 通过提供类似桌面应用的交互功能(拖拽, 自动完成和实时编辑), 它们可以帮助你无需编写复杂的javascript代码就可以提高用户体验. 下面部分将描述这些用于复杂交互的辅助函数, 并给出简单的例子. 其他的参数和技巧请参考script.aculo.us的文档.

CAUTION If complex interactions are possible, they need extra time for presentation tweaking to make them feel natural. Use them only when you are sure that they enhance the user experience. Avoid them when there is a risk that they will disorient users.注意 如果一定要用复杂的交互, 要知道这需要为界面显示花费额外的时间以达到自然的效果. 只有你确信这样有助于提高用户体验时,才使用复杂的交互. 如果有误导用户的风险,就应该避免使用.

TOP

Autocompletion 自动完成

当用户在一个文本录入组件中输入字符时, 如果能列出与用户输入匹配的一列词汇时, 就称为自动完成.在远程动作以HTML 列表(list)形式返回响应时,避免利用一个input_auto_complete_tag() helpler, 你就可以 达到这个效果. 表11-30是一个例子.

表11-30 与自动完成标记兼容的响应
<ul>
  <li>suggestion1</li>
  <li>suggestion2</li>
  ...
</ul>

你可以仿照表11-31中的例子, 在输入规则的文本时, 将辅助函数插入模板.

表11-31 在模板中使用自动完成标记helpler.
[php]
<?php echo form_tag('mymodule/myaction') ?>
  Find an author by name:
  <?php echo input_auto_complete_tag('author', 'default name',
    'author/autocomplete',
    array('autocomplete' => 'off'),
    array('use_style'    => true)
  ) ?>
  <?php echo submit_tag('Find') ?>
</form>

当用户每次在author域中输入一个字符, 它就会调用author/autocomplete动作.你可以设计动作,以便根据author请求参数确定一个可能的匹配列表, 并且以类似表11-30的格式返回.然后辅助函数就会在author标记下显示这个列表, 当用户点击其中的一个提示或用键盘选择一个提示时, 输入就可以完成. 图11-3 是它的图示.

图11-3一个自动完成的例子



input_auto_complete_tag() 辅助函数的第三个参数可以取以下参数:
use_style: 自动控制响应列表的形式.
frequency: 周期性调用的频率(默认值为0.4秒)..
tokens: 开通标记化的递增自动完成功能. 例如, 如果你设定这个参数为,, 而用户输入了jane,george, 则动作只接受'george'值.

NOTE The input_auto_complete_tag() helper, like the following ones, also accepts the usual remote helper options described earlier in this chapter. In particular, it is a good habit to set loading and complete visual effects for a better user experience.注意 和其它辅助函数一样, input_auto_complete_tag() 辅助函数也接受本章前面描述的常用的远程辅助函数选项. 值得一提的是, 为了得到更好的用户体验, 设定loading 和complete视觉效果是一个好的习惯.

TOP

发新话题