مشکل در قسمت Tree

🔧 مشکل عملکرد در Tree ناحیه‌ی Oracle APEX

در Oracle APEX، استفاده از ناحیه Tree ممکنه باعث کندی بشه. دلیلش اینه که:

  • ناحیه Tree همه رکوردهایی که از کوئری برمی‌گردن رو یک‌جا بارگذاری می‌کنه.

  • ولی توی نواحی دیگه مثل گزارش تعاملی یا گرید تعاملی، فقط اطلاعات همون صفحه لود می‌شن و بقیه داده‌ها وقتی نیاز باشه (مثلاً با رفتن به صفحه بعد) بارگذاری می‌شن.

📉 نتیجه:
اگه داده‌ها زیاد باشن، لود شدن ناحیه Tree ممکنه چند ثانیه طول بکشه و صفحه دیر باز بشه.


🕒 راه‌حل اول: Lazy Loading

با فعال کردن گزینه‌ی Lazy Loaded، اول صفحه نمایش داده می‌شه و بعد ناحیه Tree بارگذاری می‌شه.
اما حتی با این روش هم همه نودهای درخت یک‌باره بارگذاری می‌شن. پس باز هم با تأخیر روبه‌رو هستیم، مخصوصاً اگه درخت خیلی بزرگ باشه.


✅ راه‌حل بهتر: بارگذاری تدریجی نودها

در این روش:

  1. فقط نودهای ریشه (Root) موقع لود صفحه بارگذاری می‌شن.

  2. وقتی کاربر روی یک نود کلیک می‌کنه تا اون رو باز کنه، فقط فرزندان همون نود به‌صورت داینامیک (پویا) از سرور دریافت می‌شن.

📌  ولی توی نواحی دیگه مثل گزارش تعاملی یا گرید تعاملی، فقط اطلاعات همون صفحه لود می‌شن و بقیه داده‌ها وقتی نیاز باشه (مثلاً با رفتن به صفحه بعد) بارگذاری می‌شن.

مراحل پیاده‌سازی

در این مثال از جدول EMP برای پیاده‌سازی بارگذاری تنبل (Lazy Load) استفاده شده.

کوئری قسمت  Tree

در قسمت Region Source ناحیه Tree، فقط نودهای ریشه باید بازیابی بشن.

ستون‌های کلیدی موردنیاز:

 

  • VAL
  • برای ارسال پارامترها به Callback آژاکس استفاده می‌شه. در اینجا مقدار کلید اصلی جدول (EMPNO) به همراه یک چک‌سام برای امنیت ارسال می‌شه.
  • LVL
  •   مقدار ۱ به اون اختصاص داده می‌شه تا به عنوان نود ریشه مشخص بشه.
  •   LEAF_STATUS
  • مقدار ۱ بهش داده می‌شه تا مشخص کنه این نود برگ نیست (یعنی ممکنه فرزند داشته باشه).

select ename as display

     , ‘fa-user ‘ as css_class

     , ‘javascript:alert(“‘ || ename || ‘”);’ as link

     , empno ||’:’|| APEX_UTIL.get_hash(apex_t_varchar2(empno, ‘PXXX_EMP_TREE’)) as val

     , mgr as parent_id

     , 1 lvl — makes it root

     , 1 leaf_status — tells apex that this node is not a leaf

  from emp

 where mgr is null

۲. تنظیمات ناحیه  Tree

اطمینان حاصل بشه که خصوصیات زیر برای ناحیه Tree به‌درستی تنظیم شدن:

 

  •   Node Value Column:

مقدار ستون VAL

  • Hierarchy (Not Computed)
  • روی «Not Computed» تنظیم بشه
  • Node Status Column (LEAF_STATUS)
  • Hierarchy Level Column (LVL)

۳. تعریف TreeNodeAdapter

 

برای کنترل نحوه‌ی بارگذاری نودها توسط ویجت TreeView، باید از یک TreeNodeAdapter سفارشی استفاده بشه.

تابع childCount

این تابع، تعداد فرزندان هر نود رو مشخص می‌کنه.اگه مقدار برگشتی null باشه، تابع fetchChildNodes اجرا می‌شه.

adapter.childCount = function(pNode) {

    if(pNode.children && pNode.children != null) {

       return pNode.children.length;

    }

    else

    {

       return null;

    }

 }

تابع fetchChildNodes

این تابع اصلی، فرزندان یک نود رو به‌صورت داینامیک از طریق AJAX دریافت می‌کنه.

.

adapter.fetchChildNodes = function(pNode, pCallback) {

    va r nodeInfo = pNode.id.split(“:”);

    v ar empno = nodeInfo[0];

    var checksum = nodeInfo[1];

    apex.server.process( // ajax to get children of a node

       “GET_TREE_CHILDREN”

     , {

          x01: empno

        , x02: checksum

     }

     , {

          success: function(data){

             var children = data.children;

             pNode.children = [];

             for(let i = 0; i < children.length; i++) {

                var childJson = children[i];

                var childNode = {

                   id: childJson.id

                 , label: childJson.label

                 , icon: childJson.icon

                 , link: childJson.link

                }

                childNode.children = [{

                   label: “Fetching infos”

                 , icon: “fa fa-refresh fa-anim-spin”

                }] // placeholder so that we have the option to expand

                pNode.children.push(childNode);

             }

             pCallback(children.length); // return result to treeview widget

          },

          error: function( jqXHR, textStatus, errorThrown ) {

             pCallback(false);

          }

       }

    );

 }

تابع setExpanded

چون APEX فقط یک‌بار تابع fetchChildNodes رو اجرا می‌کنه، باید وقتی یک نود (گره) رو باز می‌کنیم، خودمون دوباره این تابع رو صدا بزنیم.

adapter.setExpanded = function(pTreeId, pNode, pExpanded) {

    if(pExpanded && pNode.lazyLoaded != true) { // we fetch async on expansion and it hasnt been loaded yet

       adapter.fetchChildNodes(pNode, function(pData){

          // get the JQuery object reference of the node for TreeView function calls

          var newNode = apex.region(options.regionStaticId).widget().treeView(“find”, {

             depth: -1

          , match: function(n) {return n.id == pNode.id;}

          });

          pNode.lazyLoaded = true; // flag to only load data once

          // we have to refresh to see the new children nodes

          apex.region(options.regionStaticId).widget().treeView(“refresh”, newNode);

          newNode = apex.region(options.regionStaticId).widget().treeView(“find”, {

             depth: -1

          , match: function(n) {return n.id == pNode.id;}

          });

          // Refresh causes the node to collapse so we expand the node again

          // have to find node again because refreshes changes the jquery object

          apex.region(options.regionStaticId).widget().treeView(“expand”, newNode);

۴. تعریف Callback AJAX

این پروسه AJAX داده‌های فرزندان نود فعلی رو به صورت JSON تولید می‌کنه.

declare    l_empno                 apex_application.g_x01 % type := apex_application.g_x01;
l_checksum              apex_application.g_x02 % type := apex_application.g_x02;
l_calculated_checksum   varchar2(4000);
l_node_json             JSON_OBJECT_T;
begin    l_calculated_checksum := APEX_UTIL.get_hash(
apex_t_varchar2(l_empno, ‘PXXX_EMP_TREE’)
);
if l_calculated_checksum != l_checksum then — security check
return;
end if;
APEX_JSON.open_object();
APEX_JSON.open_array(‘children’);
for node_child in (

select
ename as display           ,
‘fa-user’ as icon_class           ,
‘javascript:alert(“‘ || ename || ‘”);’ as link           ,
empno || ‘:’ || APEX_UTIL.get_hash(
apex_t_varchar2(empno, ‘PXXX_EMP_TREE’)
) as val
from
emp e1        where mgr = l_empno
) loop       APEX_JSON.open_object();
APEX_JSON.write(‘label’, node_child.display);
APEX_JSON.write(‘icon’, node_child.icon_class);
APEX_JSON.write(‘link’, node_child.link);
APEX_JSON.write(‘id’, node_child.val);
APEX_JSON.close_object();
end loop;
APEX_JSON.close_array();
–children
APEX_JSON.close_object();
end;

قابلیت‌های قابل توسعه

 

نمایش شرطی گزینه‌ی گسترش:

با کمی تغییر در منطق، فقط زمانی نود  Dummy اضافه بشه که نود واقعاً فرزند داشته باشه.

استفاده از کلیدهای مرکب (Compound Keys):

اگه کلیدها از چند ستون بیان، می‌تونیم اون‌ها رو با “:” به هم بچسبونیم یا به شکل JSON در ستون VAL ذخیره کنیم.