在 null 上调用成员函数 setComponentContext() - OctoberCMS

Call to a member function setComponentContext() on null - OctoberCMS

在过去的几个月里,我一直在学习 PHP 并使用 Octobercms,但我对它还是很陌生。我要实现的基本概念是一个包含 5 个图块的页面。当我单击图块时,它会根据您单击的图块将当前页面替换为部分页面,并更改 url 而无需重新加载页面。每个图块都有一个 id,其中包含它应该 return 的部分名称。例如,设置图块有一个 data-id="settings"。这是页面的屏幕截图

page

我为此创建了自己的插件并将该组件放置在页面上。在渲染它时 returns 仪表板部分可以正常工作并显示在上面的屏幕截图中,问题是当我单击另一个图块时。它调用 ajax 并在我的组件中调用我的 "test" 方法,并将包含部分名称的图块 ID 传递给 return。我使用 $this->renderPartial('partialName') 方法,但出现以下错误

the error

Call to a member function setComponentContext() on null

以下是我其余代码的屏幕截图:

My Javascript

$( document ).ready(function() {

    $(document).on('click','.tile',function() {

        let page = $(this).attr('id');

        history.pushState({page}, '', 'planner/' + page );

        getPage(page);

    });

    window.onpopstate = function(e){

        if(e.state){
        getPage(e.state.id);
    }

    };

    history.replaceState({page: null}, 'Default state', './planner');

    function getPage (page) {

        $.ajax({
            url: '/planner/' + page,
            type: 'GET',
            success: function(data){
                $('.page-container').html(data);
            },
            error: function(data) {
                console.log('Could not return page');
            }
        });

    }



});

My Router.php

<?php

Route::get('/planner/{page}', 'myName\budgetplanner\Components\app@test');

My Component

<?php

namespace myName\budgetplanner\Components;

use Db;

class app extends \Cms\Classes\ComponentBase
{
    public function componentDetails()
    {
      return [
            'name' => 'budgetplanner',
            'description' => 'Manage your finances.'
        ];
    }

    public function onRender()
    {
      echo ( $this->renderPartial('@dashboard.htm') );
    }

    public function test($page)
    {

      if ($page == 'undefined') {
        echo ( $this->renderPartial('@dashboard.htm') );
      }
      elseif ($page == 'overview') {
        echo ( $this->renderPartial('@overview.htm') );
      }
      elseif ($page == 'month') {
        echo ( $this->renderPartial('@month.htm') );
      }
      elseif ($page == 'reports') {
        echo ( $this->renderPartial('@reports.htm') );
      }
      elseif ($page == 'budget') {
        echo ( $this->renderPartial('@budget.htm') );
      }
      elseif ($page == 'settings') {
        echo ( $this->renderPartial('@settings.htm') );
      }

    }

}

我试着做了一些测试,认为我已经找到了问题,但我真的不明白如何解决它。这里有一些额外的截图

我转储组件对象 testing component 在渲染上看起来不错 onRender 现在它是空的? clicked settings page

这是处理 Ajax 请求的错误方式,因为您正在破坏 October CMS 页面的生命周期。

您收到错误是因为您直接要求组件处理请求 Route::get('/planner/{page}', 'myName\budgetplanner\Components\app@test');

因为 renderpartial 需要控制器上下文,如果你这样做 route 它肯定会 yield unexpected behaviour

Ok, We get this, BUT then , How to do it correctly ?


Your url lets say we use like this /planner/:type

Add framework and extra to sure layout for ajax-framework [ make sure you add them before your script ]

<script src="{{ 'assets/javascript/jquery.js'|theme }}"></script>
{% framework extras %}

Your script should look like this

<script>
$(document).ready(function() {
    
    $(document).on('click','.tile',function() {
    
        let page = $(this).attr('data-tile');
        history.pushState({page}, '', '/planner/' + page );
        getPage(page);
    });

    window.onpopstate = function(e){       
        if(e.state){
            getPage(e.state.page);
        }
    };
    // not sure causing issues so commented
    // history.replaceState({page: null}, 'Default state', './planner');
    function getPage (page) {
        $.request('onRenderTile', { data: { type: page }})
    }
});
</script>

Your Component

public function onRender()
{
    // if type is not passed default would be dashboard
    $type = $this->param('type', 'dashboard');
    return $this->onRenderTile($type)['#tile-area'];
}

public function onRenderTile($type = null)
{
    $availableTiles = [
        'dashboard',
        'tile1',
        'tile2',
        'tile3',
    ];

    // if not passed any value then also check post request
    if(empty($type)) {
        $type = post('type');
    }

    // we check partial is valid other wise just return dashboard content
    if(in_array($type, $availableTiles)) {
        return ['#tile-area' => $this->renderPartial('@'.$type.'.htm')];
    }
    else {
        return ['#tile-area' => $this->renderPartial('@dashboard.htm')];
    }
}

Your patials

文件:_tiles.htm

<div class="tile" data-tile="dashboard">Dashboard</div>
<div class="tile" data-tile="tile1">Tile 1</div>
<div class="tile" data-tile="tile2">Tile 2</div>
<div class="tile" data-tile="tile3">Tile 3</div>

文件:dashboard.htm

<div id="tile-area">
    {% partial __SELF__~"::_tiles" %}
    <h1>Dashboard</h1>
</div>

文件:tile1.htm

<div id="tile-area">
    {% partial __SELF__~"::_tiles" %}
    <h1>Tile 1 Content</h1>
</div>

文件:tile2.htm

<div id="tile-area">
    {% partial __SELF__~"::_tiles" %}
    <h1>Tile 2 Content</h1>
</div>

文件:tile3.htm

<div id="tile-area">
    {% partial __SELF__~"::_tiles" %}
    <h1>Tile 3 Content</h1>
</div>

Initially it will render default partial dashboard if no type is passed

dashboardother tile partials.

的所有其他 tile linksdashboard content 相同

现在如果你 click any tile 它会 fire October Ajax framework request 处理程序 onRenderTiletype (dashboard|tile1|tile2 ...) 所以它会正确调用 October lifecycle methods 并最终通过 [= 呈现部分31=] 和 return json#tile-area 并且作为值它将 return content of posted partial name(type)

OctoberCMS ajax framework 足够聪明,它只会用给定的 id 替换此内容,因此它会搜索 #tile-area 并用新内容替换它的内容。

有关使用 ajax 更新部分的更多信息,您可以阅读此内容:https://octobercms.com/docs/ajax/update-partials

如有任何疑问或问题,请发表评论。