在atk4中,如何使用ajax更新视图
我当前定义了一个页面,它按行显示一些数据。在每行的末尾,有一个视图显示从 mysql 中提取的总计。
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->setPoints($row['points_left'])
->setBacklog($row['backlog_ref'])
->setID($row['id'].'-points-left');
视图是用这样的模板定义的。
<!-- points left -->
<div class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog?></div>
<div id='<?$name?>' class='big_points big_point_margin'><?$pointsleft?></div>
</div>
</div>
<!-- end 0000-points-left -->
填充视图的数据是使用循环遍历的页面中的 sql 选择的,/lib/view/pointsleft.php 代码设置了从页面传递参数并更新的方法模板中的字段。
class View_PointsLeft extends View_Sticky {
function init(){
parent::init();
}
function setPoints($points){
$this->template->set('pointsleft',$points);
return $this;
}
function setBacklog($backlog){
$this->template->set('backlog',$backlog);
return $this;
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
我想在页面上发生更改时更新数据库,并更新总视图(以减少计数器)。
首先,我想知道我是否以错误的方式处理这个问题(每个视图都应该是独立的)-我应该将 id 字段传递给视图,将相关模型附加到 lib/view/pointsleft.php 内的视图并使用模型值调用设置字段?
其次,如果我以这种方式更改它,那么当使用 ajax 更改数据库值时,是否可以更轻松地使用特定 id 更新视图,如果是这样,我该怎么做?
第三 - 如果我还想根据客户端 JavaScript 上的操作触发数据库更新,我应该将此代码放在哪里,例如在我的代码的非 atk4 版本中,我有一个名为 using $.post(" update.php") 这将更新 mysql。我应该把这样的脚本放在 ATK4 的哪里?
提前致谢。
罗马人回答后更新
伙计,ATK4 太棒了! - 它的作用超出了我的预期,我正忙着在视图内创建函数来填充每个字段名称,所以现在使用 addModel 重做了它,
来自页面的调用看起来像这样,
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
模板/视图看起来像这样
<div id='<?$name?>' class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog_ref?></div>
<div class='big_points big_point_margin'><?$points_left?></div>
</div>
</div>
,lib/view 看起来像这样代码看起来像这样
<?php
class View_PointsLeft extends View_Sticky {
function loadData($id){
$this->setModel('Story')->loadData($id);
}
function init(){
parent::init();
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
更新 Romans 的代码示例
在遵循 Romans 提供的代码示例之后,我现在使用 jquery 选择器在页面代码底部添加 URL 调用,并执行一些 jiggery pokery 来获取任务和 id 的状态字段(不确定是否使用 HTML5 only stufff 使用 data-id,因此只需设置普通 id 并从中提取)。以前,删除代码位于我自己的 univ.js 脚本中,但我无法从那里访问 php 变量,因此我将其移动到页面中,
$p->js(true)->_selector('.movable')->draggable();
$p->js(true)->_selector('.target')->droppable(array(
'drop'=>$this->js(null,'function(event,ui){'.
' target=$(this).attr("id").split("-");'.
' zone=target[2];'.
' sticky=$(ui.draggable).attr("id").split("-");'.
' task=sticky[1];'.
' switch (zone) {'.
' case "verify": newStatus="V";'.
' break;'.
' case "in": newStatus="P";'.
' break;'.
' case "to": newStatus="I";'.
' break;'.
' case "done": newStatus="D";'.
' break;'.
'}
$.univ().ajaxec({ 0:"'.$this->api->getDestinationURL().'",'.
'task: task, status: newStatus }); } ')
));
并且我有一个 if 块,在页面中看起来像这样。我添加 Model_Task 并根据 GET 参数加载值,这样我还可以获得更多信息,包括与之相关的故事,这样如果状态现已完成,我还可以更新点。
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status & ($new_status=='D' | $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($old_status='D')
{
$s->set('points_left',$s->get('points_left')+$task_points);
} else {
$s->set('points_left',$s->get('points_left')-$task_points);
}
$s->update();
$story=$t->get('story_id');
}
$t->set('status',$new_status);
$t->update();
}
然后我可以计算新的点数并用剩余的点数更新故事,并通过设置模型值和使用 update() 使用 new_status 更新任务。
如果我现在移动其中一个可拖动对象,它可以工作,但会打开一个新窗口,再次显示整个页面并报告
AJAXec 响应中的错误:语法错误:语法错误
我认为打开额外的窗口是因为该错误但该错误与具有整个页面的所有 html 的响应有关。我实际上不想从 ajax 调用中重新加载,除非状态是特定的。
另外,我需要做的最后一件事就是仅在页面上重新加载已更新的特定故事的一个视图。
我尝试通过创建一个数组并在页面首次加载时向其中添加短变量
$this->pl_arr[$row['id']]=$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
,然后在处理 GET 时在 if 块中添加短变量来调用它,
$pleft=$this->pl_arr[$story];
$pleft->js()->reload()->execute();
但它失败并出现错误
Error in AJAXec response :语法错误:缺少;声明之前 致命错误:在第 247 行对 C:\wamp\www\paperless\page\scrumwall.php 中的非对象调用成员函数 js()
最终更新
最后一个错误是因为我没有在我想要更新的整个视图的外部 div 中使用 id 造成的。一旦我改变了它,它就不再为空了。
因此,第一次加载页面时,我将所有视图名称存储在循环中的关联数组中,然后将它们放在页面上
$st = $p->add('Model_Story');
$result = $st->getRows();
foreach ($result as $row) {
if (is_array($row)) {
$r=$p->add('View_Scrumwall_StoryRow')
->setWorkspace('ws-'.$row['id']);
... other code here ...
$points_left[$row['id']]=$r->add('View_PointsLeft', null, 'pointsleft')
->loadData($row['id']);
}
,然后使用像这样的 if GET 块
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status && ($new_status=='D' || $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($new_status=='D')
{
$s->set('points_left',$s->get('points_left')-$task_points);
} else {
$s->set('points_left',$s->get('points_left')+$task_points);
}
$s->update();
$story=$t->get('story_id');
//reload the points left sticky note for the story of the task
$js[]=$points_left[$story]->js()->reload();
}
$t->set('status',$new_status);
$t->update();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
}
请注意,如果我只想更新页面上的一个视图页面,我可以通过重新加载调用该对象并执行例如
$pl->js()->reload()->execute
但如果我想更新页面上的多个视图,我需要将它们放在在一个数组中(这里称为js[]),然后像这样调用执行 - 您还可以在 Roman 的键盘示例中看到这样的示例。
$js[]=$points_left[$story]->js()->reload();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
ATK4 解决了问题:)
I currently have a page defined which displays some data in rows. At the end of each row, there is a view which shows a total which is extracted from mysql.
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->setPoints($row['points_left'])
->setBacklog($row['backlog_ref'])
->setID($row['id'].'-points-left');
The view is defined with a template like this
<!-- points left -->
<div class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog?></div>
<div id='<?$name?>' class='big_points big_point_margin'><?$pointsleft?></div>
</div>
</div>
<!-- end 0000-points-left -->
The data to populate the view is selected using a sql in the page which is looped through and the /lib/view/pointsleft.php code has set methods which are passed parameters from the page and update the fields in the template.
class View_PointsLeft extends View_Sticky {
function init(){
parent::init();
}
function setPoints($points){
$this->template->set('pointsleft',$points);
return $this;
}
function setBacklog($backlog){
$this->template->set('backlog',$backlog);
return $this;
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
I want to update the database when something is changed on the page and also update the total view (to decrement the counter).
First, I'm wondering if i have approached this the wrong way (should each view should be self contained) - should i just pass the id field to the view, attach the relevant model to the view inside lib/view/pointsleft.php and call the set fields using the model values ?
Secondly, If i change it that way, does it then make it easier to update the view with a particular id when the database value is changed using ajax and if so , how do i do this ?
Thirdly - if i want to also trigger an update into the database based on an action on the client side javascript, where would i put this code e.g. in the non atk4 version of my code, i had a script called using $.post("update.php") which would update mysql. Where would i put such a script in ATK4 ?
Thanks in advance.
Update after answer from Romans
Man, ATK4 rocks ! - it does more than i expected and i was busy creating functions inside the view to populate each field name, so now having redone it using addModel,
the call from the page looks like this
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
the templates/view looks like this
<div id='<?$name?>' class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog_ref?></div>
<div class='big_points big_point_margin'><?$points_left?></div>
</div>
</div>
and the lib/view code looks like this
<?php
class View_PointsLeft extends View_Sticky {
function loadData($id){
$this->setModel('Story')->loadData($id);
}
function init(){
parent::init();
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
Update after code example from Romans
After following the code example Romans provided, i now add the URL call using jquery selectors at the bottom of my page code and do some jiggery pokery to get the task and status from the id fields (not sure about using HTML5 only stufff using data-id so just set the normal id and extract from that). Previously the drop code was in my own univ.js script but i dont have access to the php variables from there so i move it into the page
$p->js(true)->_selector('.movable')->draggable();
$p->js(true)->_selector('.target')->droppable(array(
'drop'=>$this->js(null,'function(event,ui){'.
' target=$(this).attr("id").split("-");'.
' zone=target[2];'.
' sticky=$(ui.draggable).attr("id").split("-");'.
' task=sticky[1];'.
' switch (zone) {'.
' case "verify": newStatus="V";'.
' break;'.
' case "in": newStatus="P";'.
' break;'.
' case "to": newStatus="I";'.
' break;'.
' case "done": newStatus="D";'.
' break;'.
'}
$.univ().ajaxec({ 0:"'.$this->api->getDestinationURL().'",'.
'task: task, status: newStatus }); } ')
));
and i have a if block which looks like this in the page. I add Model_Task and load the values based on the GET parameter so i then also have more information including the story it relates to so i can also update the points if the status is now done.
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status & ($new_status=='D' | $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($old_status='D')
{
$s->set('points_left',$s->get('points_left')+$task_points);
} else {
$s->set('points_left',$s->get('points_left')-$task_points);
}
$s->update();
$story=$t->get('story_id');
}
$t->set('status',$new_status);
$t->update();
}
i can then calculate the new number of points and update the story with points left and update the task with the new_status by setting the model values and using update().
If i now move one of the draggables, it works but opens a new window showing again the whole page and reporting
Error in AJAXec response: SyntaxError: syntax error
I think opening the extra window is because of the error but the error is something to do with the response having all the html for the whole page. I dont actually want any reload from the ajax call unless the status is a particular one.
Also the last thing i need to do is only reload one view on the page for the particular story that was updated.
I've tried by creating an array and adding the short variables to it like this when the page is first loaded
$this->pl_arr[$row['id']]=$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
and then in the if block while processing the GET, to recall it
$pleft=$this->pl_arr[$story];
$pleft->js()->reload()->execute();
but it fails with an error
Error in AJAXec response: SyntaxError: missing ; before statement
Fatal error: Call to a member function js() on a non-object in C:\wamp\www\paperless\page\scrumwall.php on line 247
Final update
The last error is caused because i didnt use for the id in the outer div of the whole view i wanted to update. Once i changed this it is no longer null.
So the first time the page is loaded, i store all the view names in an associative array in a loop as i put them on the page
$st = $p->add('Model_Story');
$result = $st->getRows();
foreach ($result as $row) {
if (is_array($row)) {
$r=$p->add('View_Scrumwall_StoryRow')
->setWorkspace('ws-'.$row['id']);
... other code here ...
$points_left[$row['id']]=$r->add('View_PointsLeft', null, 'pointsleft')
->loadData($row['id']);
}
and then have the if GET block like this
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status && ($new_status=='D' || $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($new_status=='D')
{
$s->set('points_left',$s->get('points_left')-$task_points);
} else {
$s->set('points_left',$s->get('points_left')+$task_points);
}
$s->update();
$story=$t->get('story_id');
//reload the points left sticky note for the story of the task
$js[]=$points_left[$story]->js()->reload();
}
$t->set('status',$new_status);
$t->update();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
}
Note that if I only want to update one view on the page, i can just call that chaing that object with reload and execute e.g.
$pl->js()->reload()->execute
but if i want to update several views on the page, i need to put them in an array (here called js[]) and then call execute like this - you can also see an example of this in Roman's codepad example.
$js[]=$points_left[$story]->js()->reload();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
Problem solved with ATK4 :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好的,为了获得更清晰的答案,我整理了一个示例:
http://codepad.agiletoolkit.org/ Dragaction.html
也许这里的例子更好地回答了问题。
就您而言,由于您正在使用模型,因此设置应该更容易。对于性能,我决定使用 2 个列表器,但理论上您也可以将每个人和任务作为一个视图。
我将关联存储在会话中(通过记忆),在您的情况下,您会将它们存储在数据库中。
Ok, for a cleaner answer, I've put together a sample:
http://codepad.agiletoolkit.org/dragaction.html
Probably example here answers the question better.
In your case, since you are working with models, it should be easier to set this up. For the performance I decided to use 2 Listers, but in theory you can also have each person and task as a view.
I'm storing associations in session (through memorize) in your case you would store them in database.
你的结构看起来没问题。如果您在其上使用 setModel() ,它将具有“pointsleft”和“backlog”字段,这些字段将自动填充。
我不知道 setID 是如何定义的,但您可以扩展 setModel,调用父级,然后执行它也。
我注意到的另一件事是,在你的模板中,最顶层的 div 应该有 id=''。这为您的视图提供了 js() 默认使用的唯一选择器。
您正在寻找的 .post 函数是 univ()->ajaxec()。它将数据发送到服务器,接收 JavaScript 并执行它,因此得名。它的行为与表单类似。
通常为了使您的代码通用,您可以将上面的代码放入视图中,但您最好使用对象的名称而不是“a”,如下所示。这消除了单独的页面处理更新的需要:
更新
澄清其执行顺序:
听起来有很多动作。实际上,每次点击会产生 2 个请求,如果您立即重新加载,则可以消除一个请求。请注意,您还可以将参数传递给 reload(),然后可以从“get”中获取该参数。我不完全理解是什么触发了原始脚本中的操作,也许我们可以在 https: //chat.stackoverflow.com/rooms/2966/agile-toolkit-atk4?
Your structure seem to be OK. If you use setModel() on it which would have "pointsleft" and "backlog" fields, those would be automatically filled in.
I don't see how setID is defined though, but you could extend setModel, call parent and then execute that too.
Another thing I noticed, is in your template the most top-level div should have id=''. This gives your view unique selector which js() uses by default.
The .post function you are looking for is univ()->ajaxec(). It sends data to the server, receives javascript and executes it, hence the name. It behaves similarly to the form.
Usually to make your code universal, you can drop this above code inside the view, but instead of 'a' you should better use the name of the object, like this. This eliminates the need for a separate page handling update:
Update
To clarify the sequence of how it's executed:
It sounds like a lot of actions. In reality that's 2 requests per click and you can eliminate one if you do reload right away. Note that you can also pass arguments to reload() which you can then fetch from "get". I don't fully understand what triggers the action in your original script, perhaps we can find this out in https://chat.stackoverflow.com/rooms/2966/agile-toolkit-atk4 ?