欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > 深入理解 SAP ABAP 动态编程:通过动态获取表内容弹出详情ALV

深入理解 SAP ABAP 动态编程:通过动态获取表内容弹出详情ALV

2024/11/30 20:48:09 来源:https://blog.csdn.net/SAP_yu/article/details/144117635  浏览:    关键词:深入理解 SAP ABAP 动态编程:通过动态获取表内容弹出详情ALV

highlight: agate
theme: github

前言:动态程序根据特定的用户输入返回结果,这些输入会发生变化,并且只能在运行时确定。本章介绍如何在 ABAP 中启用动态编程程序。

场景

软件应用程序的开发是为了解决问题或帮助用户完成日常活动。大多数情况下,问题是事先已知的,应用软件的设计和开发重点是解决手头的问题。例如,假设贵公司采用的是供应商管理库存的模式,在这种模式下,如果货物在 30 天内没有售出,公司就需要向供应商支付账单。此时,公司可以付费保留库存,也可以将库存退还给供应商。

为了适应这一业务流程,你需要开发一套应用程序,让用户能够在货物到达仓库时捕捉信息,并开发一份能够显示库存账龄的报表。该报表可帮助主管检查供应商合同期即将结束的货物。

要开发满足这一要求的应用程序,我们必须在设计时就知道需要在程序中处理的所有信息。例如,我们知道要更新哪些表中的数据,也知道要查询哪些表来计算报表中的库存账龄。所有这些信息都可以在设计时静态(事先)获得。

要显示库存的账龄,我们可以编写如下所示的代码:

SELECT item_id, item_name, in_date INTO it_inv FROM ztabinv WHERE in_date LE @p_date.WRITE : / 'The following items exceed 30 days'.
LOOP AT it_inv INTO wa_inv.WRITE: / wa_inv-item_id, wa_inv-item_name, wa_inv-in_date.
ENDLOOP.

要编写如上所示的代码,我们需要静态地知道表的名称和字段。在程序中,我们静态定义数据对象,并通过寻址数据对象的名称静态访问它们。

不过,有时我们可能要到运行时才能获得所有信息。如果我们需要编写选择查询的表取决于前一条语句的结果,该怎么办?

在查询中静态包含表名是不可能的;为了处理这种情况,我们使用动态编程

动态编程

动态编程有多种处理方式。有时,一个程序可能主要包含静态元素,但其中的某些部分可能是动态的,或者整个程序本身就是动态的。有时,你甚至需要动态地创建新程序或动态地调用过程。在本章中,我们将探讨如何处理所有这些情况。

ABAP 支持各种使 ABAP 程序动态化的概念。比如字段符号。字段符号就像指针一样,可以动态地指向程序中的任何数据对象,从而可以在运行时动态地决定访问哪个数据对象。

接着我们将讨论数据引用,与字段符号类似,数据引用允许动态指向程序中的现有数据对象。不过,数据引用也允许你动态创建数据对象。动态编程的另一个重要方面是动态确定现有数据对象的类型信息或动态创建数据类型的能力

然后要讨论运行时类型服务 (RTTS),并探讨如何动态地识别和创建数据类型。

业务需求有时要求您动态地确定从哪个数据库表获取数据,在程序流程中应调用哪个程序/事务,或类似的东西。ABAP 允许您通过在代码中动态提供标记来动态确定数据库表、存储过程、程序、事务等。

然后学习动态 token 规范,学习动态过程调用(dynamic procedure calls)。你不仅可以使 ABAP 程序中的 ABAP 代码动态化,还可以动态生成完整的 ABAP 程序本身。 最后,我们将演示一个动态程序生成。

Field Symbols

Field Symbols,中文名翻译为字段符号。字段符号类似于指针,指向现有的数据对象。字段符号不是一个数据对象,它本身不保存任何内存。相反,当一个数据对象被分配给一个字段符号时,它指向被分配的数据对象的内存位置。

字段符号是指定数据对象的标签,可以像访问数据对象本身一样访问它。例如,假设有一个内部表,其中有三个字段 FIELD1FIELD2FIELD3,我们要修改内部表的第三行。如下的代码将处理这种情况:

DATA wa LIKE LINE OF itab.wa-field1 = 'ABC'.
wa-field2 = 'XYZ'.
wa-field3 = 123.MODIFY itab FROM wa INDEX 3.

上面的代码,我们通过使用工作区 wa 来修改内表记录。在这里,waitab 是两个独立的内存位置,我们使用 MODIFY 语句将 wa 中的内容复制到 itab 中。

如果利用字段符号,我们如何达到同样的修改内表效果呢?代码如下:

FIELD-SYMBOLS: <wa> LIKE LINE OF itab.READ TABLE itab ASSIGNING <wa> INDEX 3.
IF <wa> IS ASSIGNED.<wa>-field1 = 'ABC'.<wa>-field2 = 'XYZ'.<wa>-field3 = 123.
ENDIF.

我们将 <wa> 字段符号定义为 itab 的一行。READ 语句将内表的第三行分配给字段符号 <wa>。此时,字段符号 <wa> 指向内部表的第三行。当我们通过字段符号更改字段时,第三行的字段会立即更改,因为字段符号直接指向第三行。

我们不必像在之前的代码中那样使用 MODIFY 语句手动复制内容,因为 <wa> 字段符号并不占用单独的内存空间,而是指向现有内存,这就是字段符号 <wa> 和工作区 wa 的不同。

将数据对象分配给字段符号后,我们就可以像处理分配的数据对象一样处理字段符号,并执行通常可以在分配的数据对象上执行的操作。几乎任何数据对象都可以分配给字段符号

正如您在上面的代码中看到的,有一种方法可以定义字段符号、为字段符号分配数据对象、在访问字段符号之前检查字段符号是否已分配等。不过,在讨论这些方面之前,我们先来看看字段符号如何帮助程序实现动态化。

使用字段符号使程序动态化

在我们的示例中,我们将使用一份报表来显示物料的一些基本信息。在这份报表中,我们将使用 SAP 列表查看器 (ALV) 对象模型来显示输出结果。我们将为用户提供双击任意单元格的功能,以便根据单元格的值过滤记录,并在显示输出结果下方的新容器中显示结果。

该报表的大部分组件都是静态的,因为我们已经知道要在输出中显示的数据以及用户对报表的输入。但是,根据用户操作过滤记录则是动态的。让我们先看看如何以静态方式实现这一目标,然后再看看如何使其动态化。

如下代码显示了使用 ALV 对象模型编写报告的代码。代码很长,但非常简单。请花些时间阅读代码并在您的系统中试用:

*&---------------------------------------------------------------------*
*& Report ZDEMO_SALV_STATIC
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT zdemo_salv_static.CLASS lcl_event_handler DEFINITION DEFERRED.
DATA: custom_container TYPE REF TO cl_gui_custom_container,splitter         TYPE REF TO cl_gui_splitter_container,container_1      TYPE REF TO cl_gui_container,so_alv           TYPE REF TO cl_salv_table,mo_alv           TYPE REF TO cl_salv_table,gr_event_handler TYPE REF TO lcl_event_handler,gr_event         TYPE REF TO cl_salv_events_table.
TYPES: BEGIN OF ty_mara,matnr TYPE mara-matnr,ersda TYPE ersda,ernam TYPE ernam,laeda TYPE laeda,mtart TYPE mtart,END OF ty_mara.
DATA: it_mara  TYPE STANDARD TABLE OF ty_mara,gv_matnr TYPE matnr.
SELECT-OPTIONS: s_matnr FOR gv_matnr.*&--------------------------------------------------------------*
*& Class LCL_EVENT_HANDLER
*&--------------------------------------------------------------*
CLASS lcl_event_handler DEFINITION.PUBLIC SECTION.METHODS : on_double_click FOR EVENT double_click OF cl_salv_events_table IMPORTING row column.
ENDCLASS. "LCL_EVENT_HANDLER*---------------------------------------------------------------*
* CLASS lcl_event_handler IMPLEMENTATION
*---------------------------------------------------------------*
CLASS lcl_event_handler IMPLEMENTATION.METHOD on_double_click.DATA : lo_sel      TYPE REF TO cl_salv_selections,ls_cell     TYPE salv_s_cell,container_2 TYPE REF TO cl_gui_container,lt_mara     TYPE STANDARD TABLE OF ty_mara,lw_mara     TYPE ty_mara.
*Get reference to the user selection.lo_sel = mo_alv->get_selections( ).
*Get the value of the cellls_cell = lo_sel->get_current_cell( ).
*Logic to filter recordsIF ls_cell-columnname EQ 'MATNR'.LOOP AT it_mara INTO lw_mara WHERE matnr EQ ls_cell-value.APPEND lw_mara TO lt_mara.ENDLOOP.ELSEIF ls_cell-columnname EQ 'ERSDA'.LOOP AT it_mara INTO lw_mara WHERE ersda EQ ls_cell-value.APPEND lw_mara TO lt_mara.ENDLOOP.ELSEIF ls_cell-columnname EQ 'ERNAM'.LOOP AT it_mara INTO lw_mara WHERE ernam EQ ls_cell-value.APPEND lw_mara TO lt_mara.ENDLOOP.ELSEIF ls_cell-columnname EQ 'LAEDA'.LOOP AT it_mara INTO lw_mara WHERE laeda EQ ls_cell-value.APPEND lw_mara TO lt_mara.ENDLOOP.ELSEIF ls_cell-columnname EQ 'MTART'.LOOP AT it_mara INTO lw_mara WHERE mtart EQ ls_cell-value.APPEND lw_mara TO lt_mara.ENDLOOP.ENDIF.CALL METHOD splitter->get_containerEXPORTINGrow       = 2column    = 1RECEIVINGcontainer = container_2.TRY.IF so_alv IS BOUND.so_alv->set_data( CHANGING t_table = lt_mara ).ELSE.CALL METHOD cl_salv_table=>factoryEXPORTINGlist_display = if_salv_c_bool_sap=>falser_container  = container_2IMPORTINGr_salv_table = so_alvCHANGINGt_table      = lt_mara.ENDIF.CATCH cx_salv_no_new_data_allowed.CATCH cx_salv_msg .ENDTRY.so_alv->display( ).ENDMETHOD. "on_double_clickENDCLASS. "lcl_event_handlerSTART-OF-SELECTION.PERFORM fill_table.CALL SCREEN 100.*&--------------------------------------------------------------*
*& Module STATUS_100 OUTPUT
*&--------------------------------------------------------------*
MODULE status_0100 OUTPUT.
* Create container objectCREATE OBJECT custom_containerEXPORTINGcontainer_name = 'CONTAINER'.
*Create splitter objectCREATE OBJECT splitterEXPORTINGparent  = custom_containerrows    = 2columns = 1.
*Split the containerCALL METHOD splitter->get_containerEXPORTINGrow       = 1column    = 1RECEIVINGcontainer = container_1.
*Get ALV Object referenceTRY.CALL METHOD cl_salv_table=>factoryEXPORTINGlist_display = if_salv_c_bool_sap=>falser_container  = container_1IMPORTINGr_salv_table = mo_alvCHANGINGt_table      = it_mara.CATCH cx_salv_msg .ENDTRY.
* Set the event handler heregr_event = mo_alv->get_event( ).CREATE OBJECT gr_event_handler.SET HANDLER gr_event_handler->on_double_click FOR gr_event.
*Display outputmo_alv->display( ).
ENDMODULE. " STATUS_100 OUTPUT
*&--------------------------------------------------------------*
*& Form FILL_TABLE
*&--------------------------------------------------------------*
FORM fill_table .SELECT matnr ersda ernam laeda mtart FROM mara UP TO 100 ROWS INTO TABLE it_mara WHERE matnr IN s_matnr.
ENDFORM. " FILL_TABLE

这段代码帮助我们从 MARA 表中选择某些字段数据并显示输出结果。我们使用 ALV 对象模型来生成 ALV 输出。 我们为程序定义了一个屏幕 100,并在其中创建了一个自定义控件,命名为 CONTAINER。 由于我们希望在同一屏幕上显示过滤后的记录,因此我们使用 CL_ GUI_SPLITTER_CONTAINER 类将容器分成两行。

我们在第一行显示完整的选定数据,在第二行显示过滤后的数据。下图显示了报表的初始显示。

用户可以双击任何单元格,在主显示屏下方的新容器中显示过滤后的记录。例如,如果用户双击上图中的物料类型 HALB,输出结果如下所示。

要根据用户的选择在新容器中显示过滤后的数据,我们需要做几件事:

  1. 识别用户的动作(双击)
  2. 确定单元格的列和值以筛选记录

当用户双击单元格时,ALV 对象模型会触发 ON_DOUBLE_CLICK 事件。在程序中,我们设置了事件处理程序,并在 LCL_EVENT_HANDLER 本地类中定义了 ON_DOUBLE_ CLICK 事件处理程序方法。在该方法中,我们将根据值过滤记录,并显示过滤后的记录,如图所示:

当用户双击时,LCL_EVENT_HANDLER 本地类的 ON_DOUBLE_CLICK 方法将被调用。这个方法中的代码正是我们感兴趣的地方。查看该方法中的代码,你会发现我们使用了 IF... ELSEIF... ENDIF 块来手动检查字段名称,并相应地过滤内部表数据。

由于无法静态地知道用户将双击哪一列,我们不得不构建 IF...ELSEIF 块,以包含内部表的所有字段。幸运的是,内部表中只有 5 个字段,所以我们只需在 IF...ELSEIF 块中逐个添加一个字段即可。

但是,如果内部表有 30 个字段或 50 个字段呢?如果内部表在今后的改进中增加了一些新字段怎么办?我们别无他法,只能更新这一逻辑以包含新增字段。在 IF...ELSEIF 块中静态列出所有字段不仅会使代码变得繁琐,而且硬编码也不是一种好的编程方法,应尽量避免使用。

一定有更好的处理方法,对吗?当然,这就是字段符号的用武之地。使用字段符号,我们可以修改 LCL_EVENT_HANDLER 本地类的 ON_DOUBLE_CLICK 方法中的代码,如下所示:

  METHOD on_double_click.FIELD-SYMBOLS: <f_field> TYPE any.DATA: lo_sel      TYPE REF TO cl_salv_selections,ls_cell     TYPE salv_s_cell,container_2 TYPE REF TO cl_gui_container,lt_mara     TYPE STANDARD TABLE OF ty_mara,lw_mara     TYPE ty_mara.lo_sel = mo_alv->get_selections( ).ls_cell = lo_sel->get_current_cell( ).LOOP AT lt_mara INTO lw_mara.ASSIGN COMPONENT ls_cell-columnname OF STRUCTURE lw_mara TO <f_field>.IF <f_field> IS ASSIGNED.IF <f_field> = ls_cell-value.APPEND lw_mara TO lt_mara.ENDIF.ENDIF.ENDLOOP.CALL METHOD splitter->get_containerEXPORTINGrow       = 2column    = 1RECEIVINGcontainer = container_2.TRY .IF so_alv IS BOUND.so_alv->set_data( CHANGING t_table = lt_mara ).ELSE.CALL METHOD cl_salv_table=>factoryEXPORTINGlist_display = if_salv_c_bool_sap=>falser_container  = container_2IMPORTINGr_salv_table = so_alvCHANGINGt_table      = lt_mara.ENDIF.CATCH cx_salv_no_new_data_allowed.CATCH cx_salv_msg.ENDTRY.so_alv->display( ).ENDMETHOD.  " on_double_click

在上列代码中,除了我们过滤记录的方式外,没有什么变化。

在上面的变动中,我们将 <FIELD> 字段符号定义为通用类型。然后在循环中,我们动态地将表格的组件分配给字段符号。根据用户双击的列,LS_CELL-COLUMNNAME 字段将具有该字段的名称。ASSIGN COMPONENT OF 语句将把该字段赋值给 <FIELD> 字段符号

从这里开始,我们需要做的就是检查单元格值并过滤记录。正如您所看到的,现在我们的过滤逻辑是动态的。无论用户双击哪一列,代码都会过滤记录。由于我们不再对任何内容进行硬编码,因此在今后的改进中为结构添加新字段时,这段代码也同样有效。

现在,您已经了解了如何使用字段符号使程序动态化,后面的文章让我们进一步详细介绍如何定义字段符号。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com