【问题标题】:in atk4, how do i use ajax to update a view在 atk4 中,我如何使用 ajax 更新视图
【发布时间】:2011-09-30 06:59:25
【问题描述】:

我目前定义了一个页面,它按行显示一些数据。在每一行的末尾,有一个视图显示从 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 调用,并做一些诡计多端的操作以从 id 字段中获取任务和状态(不确定是否仅使用 HTML5使用 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 响应中的错误:SyntaxError:语法错误

我认为打开额外的窗口是因为错误,但错误与包含整个页面的所有 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();

但它失败并出现错误

AJAXec 响应中的错误:SyntaxError: missing ;声明之前 致命错误:在第 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()->执行

但是如果我想更新页面上的几个视图,我需要将它们放在一个数组中(这里称为 js[]),然后像这样调用 execute - 你也可以在 Roman 的 codepad 示例中看到一个示例。

        $js[]=$points_left[$story]->js()->reload();

        $js[]=$this->js()->reload();
        $this->js(null,$js)->execute();

用 ATK4 解决的问题 :)

【问题讨论】:

  • 您收到“AJAXec 中的错误”的原因是因为返回的是 HTML 而不是 JavaScript,换句话说,->execute 没有被调用。您是说您确实添加了它,但是您的应用程序流程中可能存在一些错误,并且它可能会转到另一个分支或发生类似的事情。将执行放在页面顶部,单击,将其向下移动,单击,再次移动,单击,从确定可行的内容开始。
  • 好的 - 你是对的,我只是在 if 语句中调用 execute,所以现在已经把它移到外面,效果很好 - 只需要弄清楚如何更新不是拖动的东西的视图(在页面的其他地方) - 在聊天室中查看注释 - 谢谢
  • 我意识到问题中有很多细节,我希望版主不希望它被简化为一个简单的问题和答案,因为我认为看到创建的错误以及原因对其他可能想做同样事情的人,这是 ATK4 的核心部分。

标签: php ajax frameworks agile atk4


【解决方案1】:

好的,为了得到更清晰的答案,我整理了一个示例:

http://codepad.agiletoolkit.org/dragaction.html

这里的例子可能更好地回答了这个问题。

在您的情况下,由于您正在使用模型,因此设置它应该更容易。对于性能,我决定使用 2 个 Listers,但理论上你也可以将每个人和任务作为一个视图。

我将关联存储在会话中(通过记忆),在您的情况下,您会将它们存储在数据库中。

【讨论】:

  • 我注意到在查看 dragaction.html 的源代码时,每个人都有相同的 id 和一个名为 data-id 的新字段,其中包含序列号。每个视图具有相同的 id 是否有问题? children('div') 是 ATK4 还是 javascript 的一部分 - 我可以传递一个 ID 以添加可拖动和可放置的或仅像 DIV 这样的 html 元素?
  • 在拖动操作中,所有列表都是一个对象,这就是为什么它是一个“id”。如果使用 ->_selector(); 指定选择器,则可以将可拖动元素添加到任何元素;
  • 好的 - 我已经对所有对象进行了可拖放操作,只是想知道为什么您的 ID 相同。没问题 - 感谢您使用 MVCLister 只是为了证明这一点,它不会影响我需要的部分。谢谢
【解决方案2】:

您的结构似乎没问题。如果你在它上面使用 setModel() ,它会有“pointsleft”和“backlog”字段,这些字段会被自动填写。

虽然我不明白 setID 是如何定义的,但你可以扩展 setModel,调用 parent,然后也执行它。

我注意到的另一件事是,在您的模板中,最顶级的 div 应该具有 id=''。这为您的视图提供了 js() 默认使用的唯一选择器。

您正在寻找的 .post 函数是 univ()->ajaxec()。它将数据发送到服务器,接收 javascript 并执行它,因此得名。它的行为类似于表单。

$mybutton->js('click')->ajaxec($this->getDestinationURL(null,array('a'=>'b'));

if($_GET['a']){
    // update database
    $r->getElement('plfat')->js()->reload()->execute();
}

通常为了使您的代码具有通用性,您可以将上面的代码放在视图中,但最好不要使用“a”,而是使用对象的名称,就像这样。这消除了对单独的页面处理更新的需要:

$this->mybutton->js('click')->ajaxec($this->getDestinationURL(null,
     array($this->name=>'reload'));

if($_GET[$this->name]){
    // update database
    $this->js()->reload()->execute();
}

更新

为了阐明它的执行顺序:

  1. 页面呈现为 HTML 发送到您的浏览器。
  2. 连同页面一起发送 Javascript 链。所有这些都定义了 js 的第一个参数,例如 js(true)、js('click')。在我的代码中我有 js('click') 所以它被发送到浏览器。
  3. 用户执行操作,例如单击按钮。这会触发 ajaxec() 函数
  4. ajaxec 函数使用您在此处指定的参数对页面执行 AJAX 请求。
  5. PHP 再次执行,但这次它进入 if() 分支。创建一个不带参数的 js() 并 ->execute() 将 javascript 发送到浏览器。
  6. 浏览器接收 js()...->execute() 的输出并对其进行评估。在我们的例子中,它包含一些其他元素的 reload()。
  7. atk4_loader 小部件用于重新加载向服务器发送 AJAX 请求的页面的其他部分
  8. PHP 使用 cut_object 参数执行。它重新初始化原始页面,但仅选择性地呈现一个对象。该对象的输出被发送回前端。
  9. PHP 还像 #2 一样重新生成 JS 链,但只与该对象相关
  10. 前端的 atk4_loader 接收代码,替换元素的 HTML 并重新评估 javascript。
  11. 返回#3

听起来像是很多动作。实际上,每次点击有 2 个请求,如果您立即重新加载,您可以消除一个。请注意,您还可以将参数传递给 reload(),然后您可以从“get”中获取这些参数。我不完全理解是什么触发了您原始脚本中的操作,也许我们可以在https://chat.stackoverflow.com/rooms/2966/agile-toolkit-atk4 中找到这个?

【讨论】:

  • 好的,所以我现在让模型根据原始问题的更新填充视图中的字段。我还将 id 移到了外部 div (它基于以前的方式 [没有 ATK4 的好处] 使用 jquery 直接更新左侧的点)。
  • 所以我想我想在特定操作上执行视图的重新加载-不是单击按钮,但我认为主体将是相同的-我需要在视图中放置哪个功能js()->reload()->execute() 的代码;以及如何从页面传递的其他地方调用它。 setID 用于传递当前行的特定 id 的 ID,所以我现在将其作为参数传递给 loadData。
  • 在 univ()->ajaxec() 调用中,我将我想要由它执行的代码放在哪里 - 这将是一个数据库更新语句,因此它将有一个模型,我想要更新 - 它是否进入没有默认模板的视图?
  • $_name?> 保留并自动填充下划线。不要与 $name?> 混淆
  • ajaxec() 执行 PHP 产生的动作,该动作以 execute() 函数结束。让我澄清一个序列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多