dt250827 2011-09-30 06:59
浏览 46
已采纳

在atk4中,我如何使用ajax更新视图

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 :)

  • 写回答

2条回答 默认 最新

  • duanpu8830 2011-09-30 14:47
    关注

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?
  • ¥88 找成都本地经验丰富懂小程序开发的技术大咖
  • ¥15 如何处理复杂数据表格的除法运算
  • ¥15 如何用stc8h1k08的片子做485数据透传的功能?(关键词-串口)
  • ¥15 有兄弟姐妹会用word插图功能制作类似citespace的图片吗?
  • ¥200 uniapp长期运行卡死问题解决
  • ¥15 latex怎么处理论文引理引用参考文献
  • ¥15 请教:如何用postman调用本地虚拟机区块链接上的合约?
  • ¥15 为什么使用javacv转封装rtsp为rtmp时出现如下问题:[h264 @ 000000004faf7500]no frame?
  • ¥15 乘性高斯噪声在深度学习网络中的应用