Классы-компоненты, контроллеры, BX.ajax.runComponentAction



пример, кусок кода
возможно, когда-нибудь систематизирую, но не факт

компонент-класс personal:security

class.php
<?php

use nikaverro\user\controller\Controller;
use Bitrix\Main\Engine\Contract\Controllerable;
use Bitrix\Main\Engine\ActionFilter\Csrf;
use Bitrix\Main\Engine\ActionFilter\HttpMethod;
use \Bitrix\Main\Application;


\Bitrix\Main\Loader::includeModule('nikaverro.user');
\Bitrix\Main\Loader::includeModule('nnikaverro.dev');

class securityPersonal  extends Controller implements Controllerable {

    public function configureActions()
    {
        return [
            'editProfile' => [
                'prefilters' => [
                    new HttpMethod(
                        array(HttpMethod::METHOD_POST)
                    ),
                    new Csrf(),
                ],
                'postfilters' => []
            ],
        ];
    }
    
    //обработка действия удаления
    public function deleteAccountAction($action, $sessid, $id){ 
        global $USER;
        //throw new \Exception("Technical error", 1);
        if($action != 'deleteAccount'){
            throw new \Exception("Technical error", 1);
        }
        if (!check_bitrix_sessid()) {
            throw new \Exception("SESSION_EXPIRED", 1);
        }
        if(!$USER->IsAuthorized()) {
            throw new \Exception("User is not authorized", 1);
        }
        if($USER->getID() == $id){
            $user = new CUser;
            $fields = Array(
                "ACTIVE" => false,
            );
            $user->Update($id, $fields);
            if($user->LAST_ERROR){
                throw new \Exception($user->LAST_ERROR, 1);
            }

        }
        else{
            throw new \Exception("You can't delete not your account", 1);
        }
        return 'Your account was successful delete! You will be redirect at the main page.';
    }


    //первоначальная загрузка формы 
    function securityController()
    {
        global $USER;
        $this->arResult["USER_ID"] = $USER->getID();
        $this->arResult["EMAIL"] = $USER->getEmail();
        $this->includeComponentTemplate("personal");
    }
}
вызов компонента на странице
$params = [
    "controller" => "security",
    "data" => $_REQUEST
];

$APPLICATION->IncludeComponent(

   'personal:security', // пространство имён компонента
   'europe', // наименование шаблона компонента
    $params
);

шаблон компонента personal.php
<div class="col-4">    
   <div class="card">
        <div class="card-header">
            <h3 class="mb-0">Delete account </h3>
        </div>
        <div class="card-body">
            <div class="mb-2">
                You can delete your <b><?=$arResult["EMAIL"]?></b> account.<br>
                Your licenses will work, but you won't be able to manage them.
            </div>
            <a href="#" class="btn btn-primary delete-account w-100" >Delete account</a>
            <script>
                $(document).ready(function() {
                    $(document).on('click', '.delete-account', function(e) {
                        e.preventDefault();
                        if (confirm('Are you sure you want to delete your account?')) {
                            var data = {
                                id : <?=$arResult["USER_ID"]?>,
                                action: 'deleteAccount',
                                sessid: BX.message('bitrix_sessid'),
                            };
                            var request = BX.ajax.runComponentAction("personal:security", "deleteAccount", {
                                mode: "class",
                                data: data
                            });

                            request.catch(function(response) {                               
                                var message = response.errors[response.errors.length - 1].message;
                                var title = "Operation failed";
                                var type = "error";
                                toastr[type](message, title, {
                                    positionClass: "toast-top-right",
                                    closeButton: true,
                                    progressBar: false,
                                    newestOnTop: true,
                                    rtl: $("body").attr("dir") === "rtl" || $("html").attr("dir") === "rtl",
                                    timeOut: 2500
                                });
                                
                            });
                            request.then(function(response) {
                                
                                if (response.status == "success") {
                                    var message = response.data;
                                    var title = "Success";
                                    var type = "success";
                                    toastr[type](message, title, {
                                        positionClass: "toast-top-right",
                                        closeButton: true,
                                        progressBar: false,
                                        newestOnTop: true,
                                        rtl: $("body").attr("dir") === "rtl" || $("html").attr("dir") === "rtl",
                                        timeOut: 2500
                                    });                                   
                                    setTimeout(function (){
                                        window.location.href = '/';
                                    },3000);
                                }
                            });
                        }
                    });
                });
            </script>           
        </div>
    </div>
</div>