`
isiqi
  • 浏览: 16034332 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

第十天--使用Ajax表单修改数据

阅读更多
通过昨天对已知技术的回顾,我们已经熟悉交互的使用。显示丰富格式的问题与列表,甚至是分页,并不足以使一个程序鲜活。而askeet概念的核心就是允许任何注册的用户询问一个新问题,而任何用户可以回答已存在的问题。现在是我们实现的时候了。

添加一个新问题

我们在第七天所创建的侧边栏已经包含一个添加新问题的链接。他链接到question/add动作,这正是我们将要开发的。

限制到注册用户的访问

首先,只有注册用户可以添加一个新问题。为了限制到question/add动作的访问,在askeet/apps/frontend/modules/question/config/目录下创建一个security.yml文件:

add:
is_secure: on
credentials: subscriber

all:
is_secure: off


当一个非注册用户试着访问一个限制动作时,Symfony将其重定向到登陆动作。这个动作必须在程序设置中进行定义,在login_module与login_action关键字之后:

all:
.actions:
login_module: user
login_action: login

关于动作访问的限制的更多信息可以在Symfony一书的安全一章进行了更多的解释。

addSuccess.php模板

question/add动作将会同时显示表单与处理表单。这就意味着要显示表单,我们只需要一个空动作。另外,如果表单验证出错,还会再次显示表单:

public function executeAdd()
{
}

public function handleErrorAdd()
{
return sfView::SUCCESS;
}

所有的动作都会输出addSuccess.php模板:

<?php use_helper('Validation') ?>

<?php echo form_tag('@add_question') ?>

<fieldset>

<div class="form-row">
<?php echo form_error('title') ?>
<label for="title">Question title:</label>
<?php echo input_tag('title', $sf_params->get('title')) ?>
</div>

<div class="form-row">
<?php echo form_error('body') ?>
<label for="label">Your question in details:</label>
<?php echo textarea_tag('body', $sf_params->get('body')) ?>
</div>

</fieldset>

<div class="submit-row">
<?php echo submit_tag('ask it') ?>
</div>
</form>

title与body控件都有一个默认值(表单帮助器的第二个参数),其值由请求的表单参数进行定义。为什么是这样呢?因为我们要为表单添加一个验证文件。如果验证失败,表单就会再次显示,而前面的用户实体仍存在于请求参数中。他们可以用作表单元素的默认值。

前面的实体并不会因为表单验证失败而丢失。这是我们对于一个用户友好程序的最低要求。

但是,为了达到这个目的,我们需要一个表单验证文件。

表单验证

在question模块下创建一个validate/目录,并且添加一个add.yml验证文件:

methods:
post: [title, body]

names:
title:
required: Yes
required_msg: You must give a title to your question

body:
required: Yes
required_msg: You must provide a brief context for your question
validators: bodyValidator

bodyValidator:
class: sfStringValidator
param:
min: 10
min_error: Please, give some more details

如果我们要了解更多的关于表单验证的信息,我们回到第6天,或是阅读Symfony一书的表单验证一章。

处理表单提交

现在编辑question/add动作来处理表单提交:

public function executeAdd()
{
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
// create question
$user = $this->getUser()->getSubscriber();

$question = new Question();
$question->setTitle($this->getRequestParameter('title'));
$question->setBody($this->getRequestParameter('body'));
$question->setUser($user);
$question->save();

$user->isInterestedIn($question);

return $this->redirect('@question?stripped_title='.$question->getStrippedTitle());
}
}

记住,->setTitle()方不同时也会设置stripped_title,而->setBody()方法同时也会设置html_body域,因为我们在Question.php模块类中重写了这两个方法。创建问题的用户将会声明对其感兴趣。这是为了避免出现没有人对其感兴趣的问题,为样就太悲哀了。

动作的结束处包含了一个->redirect()方法来重定向到创建问题的详细页面。比起->forward()方法,其最大的优点在于,如果用户以后刷新详细问题页面,表单不会再次提交。另外,'back'按钮也会按所希望的方式工作。这是一条通常的规则:我们不要使用->forward()方法来结束表单提交的处理动作。

最好是如果表单不是在POST模式,动作仍然可以显示表单。其形为正如同我们在前面编写的空动作,返回sfView::SUCCESS,启动addSuccess.php模板。

不要忘记在User模块中创建珍上isInterestedIn()方法:

public function isInterestedIn($question)
{
$interest = new Interest();
$interest->setQuestion($question);
$interest->setUserId($this->getId());
$interest->save();
}

作为一次重构,我们可以在user/interested动作中使用这个方法来替换完在同样事情的代码。现在可以进行测试了。使用一个测试用户,我们可以添加一个问题。

添加一个新答案

答案的添加将会以一种略微不同的方式来实现。在这里并没有必要使用一个表单将用户重定向到一个新页面,而是再一次到要显示答案的另一个页面。所以新的答案表单应是Ajax形式,而且新答案应立即显示在问题详细页面。

添加一个Ajax表单

用下面的代码来更改modules/question/templates/showSuccess.php模板的结束处:

...
<div id="answers">
<?php foreach ($question->getAnswers() as $answer): ?>
<div class="answer">
<?php include_partial('answer/answer', array('answer' => $answer)) ?>
</div>
<?php endforeach; ?>

<?php echo use_helper('User') ?>

<div class="answer" id="add_answer">
<?php echo form_remote_tag(array(
'url' => '@add_answer',
'update' => array('success' => 'add_answer'),
'loading' => "Element.show('indicator')",
'complete' => "Element.hide('indicator');".visual_effect('highlight', 'add_answer'),
)) ?>

<div class="form-row">
<?php if ($sf_user->isAuthenticated()): ?>
<?php echo $sf_user->getNickname() ?>
<?php else: ?>
<?php echo 'Anonymous Coward' ?>
<?php echo link_to_login('login') ?>
<?php endif; ?>
</div>

<div class="form-row">
<label for="label">Your answer:</label>
<?php echo textarea_tag('body', $sf_params->get('body')) ?>
</div>

<div class="submit-row">
<?php echo input_hidden_tag('question_id', $question->getId()) ?>
<?php echo submit_tag('answer it') ?>
</div>
</form>
</div>

</div>

重构

link_to_login()函数必须添加到UserHelper.php帮助器中:

function link_to_login($name, $uri = null)
{
if ($uri && sfContext::getInstance()->getUser()->isAuthenticated())
{
return link_to($name, $uri);
}
else
{
return link_to_function($name, visual_effect('blind_down', 'login', array('duration' => 0.5)));
}
}

这个函数所做的事情是我们在其他的User帮助器中已经看到过的:如果用户已被授权,将会显示一个到动作的链接,而如果没有授权,这个链接指向Ajax登陆表单。所以在link_to_user_interested()和link_to_user_relevancy()函数中使用link_to_login()来代替link_to_function()函数。不要忘记在modules/sidebar/templatetes/defaultSuccess.php中到@add_question的链接。是的,这就是重构。

处理表单提交

虽然他仍是调用代码片段,我们在这里选择的用来处理Ajax请求的方法与我们在第八天所描述的还是有一些略微的不同。这是因为我们希望表单提交的结果实际替换表单。这就是为什么将form_remote_tag()帮助器中的更新参数指向表单本身的容器,而不是指向外层空间。_answer.php片段将会包含在答案添加动作的结果中,所以最终的结果看起来如同下面的样子:

...
<div id="answers">
<!-- Answer 1 -->
<!-- Answer 2 -->
<!-- Answer 3 -->
...
</div>

<div class="answer" id="add_answer">
<!-- The new answer -->
</div>


也许我们已经猜到了form_remote_tag() javascript是如何工作的了:他通过一个XMLHttpRequest对象将表单提交给url参数中指定的动作。动作的结果替换更新参数中指定的元素。而且,与第八天中的link_to_remote()帮助器相类似,他会激活活动指示器可见,并且依据请求的提交使其不可邮,而且在Ajax事务的结束处高亮显示更新的部分。

在这里我们要多说几名关于与一个新问题相关联的用户。我们在前面提到,答案必须链接到一个用户。如果用户已经被验证,那么他的user_id就会用于新答案。否则,就会使用anonymous,除非用户选择登陆。位于GlobalHelper.php帮助器中的link_to_login()帮助器就会激活布局中的隐藏表单。我们可以游览askeet源友来查看其代码。

answer/add动作

作为Ajax表单的url参数的@add_answer规则指向answer/add动作:

add_answer:
url: /add_anwser
param: { module: answer, action: add }

下面是动作的内容:

public function executeAdd()
{
if ($this->getRequest()->getMethod() == sfRequest::POST)
{
if (!$this->getRequestParameter('body'))
{
return sfView::NONE;
}

$question = QuestionPeer::retrieveByPk($this->getRequestParameter('question_id'));
$this->forward404Unless($question);

// user or anonymous coward
$user = $this->getUser()->isAuthenticated() ? $this->getUser()->getSubscriber() : UserPeer::retriveByNickname('anonymous');

// create answer
$this->answer = new Answer();
$this->answer->setQuestion($question);
$this->answer->setBody($this->getRequestParameter('body'));
$this->answer->setUser($user);
$this->answer->save();

return sfView::SUCCESS;
}

$this->forward404();
}
首先,如果这个动作不是在POST模式下调用的,这就意味着是某些人在浏览器的地址栏中输入的。动作并不是为这种请类型而设置的,所以在这种情况下会返回一个404错误。

为了确定要设置为答案作者的用户,动作会检测当前用户是否已被授权。如果不是这种情况,动作就会通地UserPeer类的new::retrieveByNickname()方法使用'Anonymous Coward'用户。如果我们对这个方法还有疑惑,我们可以查看其源代码。

这样,就准备好了所有事情来创建一个新问题,并且将请求发往addSuccess.php模板。正如我们所希望的,这个模板只包含一行代码:

<?php include_partial('answer', array('answer' => $answer)) ?>

我们同时需要在frontend/modules/answer/config/view.yml中禁止这个动作的布局:

addSuccess:
has_layout: off

最后,如果用户提交一个空答案,我们并不要进行存储。所以就会略地数据处理部分,而动作并不会返回任何内容,这将简单的清除页面。我们已经在Ajax表单中完成了数据处理,但是这应将表单自身放在另一个片段中,而现在并不值得这样做。

测试

这样就完成了?正是,现在Ajax表单已经可以使用了,干净,安全。我们现在可以进行测试,显示一个问题的答案列表,并且可以添加一个新问题。这个页面并不需要刷新,而新答案显示在前面的列表的底部。这很简单,不是吗?

明天见
分享到:
评论

相关推荐

    ThinkPHP3.2仿京东商城视频教程实战课程,ThinkPHP3.2开发大型商城项目实战视频

    第十一天 1.下定单-1 2.下定单-2 3.只能购买在购物车中勾选的商品 4.支付宝支付-1-制作去支付宝的按钮 5.支付宝集成-2-完成 6.网站上线并在QQ互联中创建一个APP 7.QQ登录集成完成 第十二天 1.后台分类筛选属性的添加...

    PHP+Ajax网站开发典型实例-源代码

    实例3 中文时间显示实例 实例4 删除字符串中的空白 实例5 字符串反转 实例6 字符串加密 实例7 检查日期的有效性 实例8 简单猜数游戏 实例9 验证信用卡号 实例10 计算两个数组并、交和差 ...第10章 Ajax实现综合实例

    【卷一/共两卷】AJAX实战pdf高清版90M

    第10章 输入前提示 10.1 考察输入前提示应用 10.1.1 输入前提示的常见特征 10.1.2 Google Suggest 10.1.3实战开发Ajax输入前提示 10.2 服务器端框架:C# 10.2.1 服务器与数据库 10.2.2 测试服务器端代码. 10.3 ...

    Ajax详解.rar

    第 10 部分: 使用 JSON 进行数据传输 111 1.1 选择的意义 112 1.2 JSON 基础 113 1.3 在 JavaScript 中使用 JSON 114 第 11 部分: 借鉴最优秀的 Ajax 应用程序 117 1.1 Google 地图 117 1.2 TaDaList 119 1.3...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    -使用Hidden来显示隐藏ExtAspNet控件,而不是使用Visible属性(Visible目前设置为只读属性)。 -使用Hidden控制Window控件的显示隐藏,Popup已经标记为Obsolete属性。 -Window的实例方法GetCloseReference等以及...

    PHP+Ajax网站开发典型实例

    PHP+Ajax网站开发典型实例.。。 ... 实例3 中文时间显示实例 实例4 删除字符串中的空白 ...第10章 Ajax实现综合实例 实例82 汇智在线留言板程序 实例83 汇智在线聊天室程序 实例84 汇智在线BBS论坛

    ExtAspNet_v2.3.2_dll

    -使用Hidden来显示隐藏ExtAspNet控件,而不是使用Visible属性(Visible目前设置为只读属性)。 -使用Hidden控制Window控件的显示隐藏,Popup已经标记为Obsolete属性。 -Window的实例方法GetCloseReference等以及...

    ASP.NET 控件的使用

    第10章 使用List控件 294 10.1 List控件概述 294 10.1.1 声明列表项 294 10.1.2 绑定到数据源 296 10.1.3 确定被选中的列表项 299 10.1.4 追加数据项 302 10.1.5 启用自动回传 303 10.1.6 使用列表项集合 304 10.2 ...

    Asp+AJAX静态分页 亲测 可用

    //从新获取新修改后的第一页的数据 setTimeout('rightinfo()',3000); //3秒后将right对象的原始字符串写入。 } else { document.getElementById('pagesize').disabled=true; //将几个FORM表单的元素都设为不可...

    最新Python3.5零基础+高级+完整项目(28周全)培训视频学习资料

    第10周 心灵分享 上节回顾 多进程 多进程Queue 多进程Pipes与Manager 进程锁与进程池详解 协程 协程Gevent 协程之爬虫 协程之Socket IO多路复用 IO模式 Select解析Socket通信 作业 第11周 鸡汤 消息队列介绍 ...

    WEB安全测试

    第10章 攻击AJAX 211 10.1 观察实时的AJAX请求 213 10.2 识别应用中的JavaScript 214 10.3 从AJAX活动回溯到源代码 215 10.4 截获和修改AJAX请求 216 10.5 截获和修改服务器响应 218 10.6 使用注入数据破坏AJAX 220 ...

    ASP.NET.4揭秘

    第10章 使用列表控件333 10.1 列表控件概述333 10.1.1 声明列表项333 10.1.2 绑定到数据源335 10.1.3 确定被选中的列表项338 10.1.4 追加数据项342 10.1.5 启用自动回传343 10.1.6 使用列表项集合345 10.2 使用...

    ASP.NET 3.5 开发大全11-15

    第10章 访问其他数据源 10.1 使用ODBC .NET Data Provider 10.1.1 ODBC .NET Data Provider简介 10.1.2 建立连接 10.2 使用OLE DB.NET Data Provider 10.2.1 OLE DB.NET Data Provider简介 10.2.2 建立连接 10.3 ...

    ASP.NET 3.5 开发大全1-5

    第10章 访问其他数据源 10.1 使用ODBC .NET Data Provider 10.1.1 ODBC .NET Data Provider简介 10.1.2 建立连接 10.2 使用OLE DB.NET Data Provider 10.2.1 OLE DB.NET Data Provider简介 10.2.2 建立连接 10.3 ...

    ASP.NET3.5从入门到精通

    9.6.2 使用GridView 显示、删除、修改数据 9.6.3 使用DataList 显示数据 9.6.4 DataList 分页实现 9.6.5 使用SQLHelper 操作数据库 9.7 小结 第 10 章访问其他数据源 10.1 使用ODBC .NET Data Provider 第一篇窗口与...

    JavaScript王者归来part.1 总数2

     12.6 读写数据--添加、修改和删除属性   12.7 外观与行为   12.7.1 DOM样式属性   12.7.2 控制DOM元素的显示与隐藏   12.7.3 改变颜色和大小--一个简单有趣的例子   12.7.4 改变位置--创建一个绕圆圈...

    ASPNET35开发大全第一章

    第10章 访问其他数据源 10.1 使用ODBC .NET Data Provider 10.1.1 ODBC .NET Data Provider简介 10.1.2 建立连接 10.2 使用OLE DB.NET Data Provider 10.2.1 OLE DB.NET Data Provider简介 10.2.2 建立连接 10.3 ...

    JavaScript完全自学宝典 源代码

    第10章(\c10) 示例描述:学习JavaScript中的正则表达式。 10.1.html 使用句点符号匹配特定字符串。 10.2.html 使用方括号符号实现匹配出租车牌号。 10.3.html 使用或符号匹配日期。 10.4.html ...

    ASP.NET 2.0+SQL Server 2005全程指南-源代码

    第10章 导航与登录 10.1 站点导航 10.1.1 Menu控件 10.I.2 SiteMapPath控件 10.1.3 TreeView控件 10.2 站点登录 10.2.1 创建新用户 10.2.2 用户登录 10.3 本章小结 第11章 母版页 11.1 母版页概述 11.1.1...

Global site tag (gtag.js) - Google Analytics