Skip to Content
Author's profile photo Jörg Krause

MVC (model view controller) framework for ABAP part 2

Reports with class

 

In MVC (model view controller) framework for ABAP part 1 you can find the “starter kit” for a framework you can use to create applications that are using dynpros and CFW controls that the framework is able to control. In this second part I will introduce only one further class that can be used for report programming. In my daily work, I am using this report very frequently as a template for new ones.

 

The demo application

 

Just like the demo application in the first part, the application contains the framework class that you can extract to a public class for reusing the code.

 

Application screen You can upload the screen 0001 from the file attached. Other than in the first part, we do not need a subscreen here. All we need is an empty screen that is able to carry the docking container in which we will place an ALV with the result list.

 

Selection screen controller The selection screen is controlled by the new class ZCL_MVCFW_CON_SELSCR. The controller derives from the class ZCL_MVCFW_CON_DYNPRO and extends it only with two methods

 

How it works

 

In order to get everything to work, a few steps are necessary. Let’s go through it step by step.

 

Selection screen interface To simplify the passing of selection screen parameters between the classes, I use always a local interface that contains a structured type with all input elements from the selection screen

 

 

You can see that I use the same names for the components as for the selection screen elements:

 

 

Global data Some global variables are indispensible:

 

 

INITIALIZATION Here the main controller is instantiated. After that, the get_con_dynpro method is called to create a controller for the actual dynpro, which is the selection screen.

 

PAI The PAI event of a selection screen is at selection-screen, so we call the PAI of the screen controller, which is a framework method that does the following:

  • Ask the main controller to fetch the screen data.
    • Therefore, the method get_screen_data of the main controller must be redefined:

      As you see, the data is stored in an attribute of the main controller. This can be useful, if you want to react to user inputs during PBO, i.e. switching on/of field attributes and so on.
  • Invoke the PAI of the super class, which will compare all component values from the screen with the stored ones in the memory of the framework class ZCL_MVCFW_DYNPRO and call PAI_FIELD_CHANGE on change of values.

 

START-OF-SELECTION The method run of the selection-screen controller is called.

 

RUN The control is passed to the main controller (run_program), where the main logic of the report begins to work.

 

Batch/Online processing As you can see in run_program, there are two branches, one for background processing and one for online. The online branch is similar to the demo in part 1. For the batch processing, a list has to be created instead of calling a screen. Therefore, a list controller has been added to the program, which we derive from the generic CFW controller.

 

 

Of course we do not have any CFW control here. But the framework class can be used even without container. In method refresh the output is coded. In this case, I use CL_SALV_TABLE to produce an output list.

 

Some notes

 

I some of my use cases, I use more than one model class because of complexity of the application. In this case, it comes handy to declare common used data types in the interface lif_report.

 

All the best

Jörg

*&---------------------------------------------------------------------*
*& Report  ZP_MVCFW_REPORT_DEMO
*&
*&---------------------------------------------------------------------*
report zp_mvcfw_report_demo.

interface lif_report.
  types:
    begin of gty_s_selscreen,
      s_carrid type range of sflight-carrid,
      s_connid type range of sflight-connid,
      s_fldate type range of sflight-fldate,
      p_batch  type abap_bool,
    end of gty_s_selscreen.
endinterface.

class lcl_model definition.

  public section.
    methods:
      read_db
        importing is_selection type lif_report=>gty_s_selscreen,
      get_out_tab
        exporting et_out type standard table.


  private section.

    data: mt_main_output type table of sflight,
          ms_selection   type lif_report=>gty_s_selscreen.

endclass.                    "lcl_model DEFINITION

class lcl_con_selscr_1000 definition deferred.

class lcl_con_main definition inheriting from zcl_mvcfw_con_main.

  public section.

    constants:
      cv_con_alv_out        type char32 value 'LCL_CON_ALV_OUT',
      cv_con_selscreen_1000 type char32 value 'LCL_CON_SELSCR_1000',
      cv_con_list           type char32 value 'LCL_CON_LIST'.
    methods:
      constructor
        importing iv_program type syrepid,
      pai_main
        importing
          iv_ucomm type syucomm,
      pbo_main redefinition,
      run_program,
      get_selections redefinition.

  protected section.
    methods:
      create_con_dynpro redefinition,
      create_one_controller redefinition.

  private section.
    data: mo_model     type ref to lcl_model,
          ms_selscreen type lif_report=>gty_s_selscreen.

endclass.                    "lcl_con_main DEFINITION

class lcl_con_selscr_1000 definition inheriting from zcl_mvcfw_con_selscr.

  public section.
    methods:
      constructor
        importing io_model      type ref to lcl_model
                  io_con_main   type ref to lcl_con_main
                  is_selections type lif_report=>gty_s_selscreen,
      run redefinition.

  protected section.
    methods:
      pai_field_change redefinition.

  private section.
    data: mo_model type ref to lcl_model,
          mo_main  type ref to lcl_con_main,
          " only for background run:
          mt_out   type table of sflight.

endclass.                    "lcl_con_selscr_1000 DEFINITION

class lcl_con_alv_out definition inheriting from zcl_mvcfw_con_alv.
  public section.
    methods:
      refresh redefinition,
      constructor
        importing
          io_container type ref to cl_gui_container
          io_model     type ref to lcl_model
          io_main      type ref to lcl_con_main
        raising zcx_mvcfw_error.

  protected section.
    methods:
      prepare_layout redefinition,
      on_double_click redefinition,
      on_toolbar redefinition,
      on_ucomm redefinition,
      prepare_exclude redefinition.

  private section.
    data: mo_model type ref to lcl_model,
          mo_main  type ref to lcl_con_main.

endclass.                    "lcl_con_alv_out DEFINITION

class lcl_con_list definition inheriting from zcl_mvcfw_con_cfw.

  public section.

    methods:
      constructor
        importing
          io_model type ref to lcl_model
          io_main  type ref to lcl_con_main,
      refresh redefinition.

  protected section.

  private section.

    data: mo_model type ref to lcl_model,
          mo_main  type ref to lcl_con_main,
          mt_out   type table of sflight.

endclass.


tables sflight.

select-options: s_carrid            for sflight-carrid,
                s_connid            for sflight-connid,
                s_fldate            for sflight-fldate.

parameters p_batch as checkbox.

data: gv_okcode     type syucomm,
      go_sel_screen type ref to lcl_con_selscr_1000,
      go_main       type ref to lcl_con_main.

*--------------------------------------------------------------------*
* report events
*--------------------------------------------------------------------*

initialization.
  " start point: create main and sel.screen controllers
  go_main ?= lcl_con_main=>get_instance( ).
  go_sel_screen ?= go_main->get_con_dynpro( ).

at selection-screen.
  " call pai of sel.screen controller
  go_sel_screen->pai( ).

start-of-selection.
  " call run routine of sel.screen controller
  go_sel_screen->run( ).

class lcl_model implementation.

  method read_db.

    ms_selection = is_selection.
    select * from sflight into table mt_main_output
      where carrid in ms_selection-s_carrid and
            connid in ms_selection-s_connid and
            fldate in ms_selection-s_fldate.

  endmethod.                    "read_db

  method get_out_tab.

    et_out = mt_main_output.

  endmethod.                    "get_out_tab

endclass.                    "lcl_model IMPLEMENTATION

class lcl_con_main implementation.
  method constructor.

    " the report name is needed in the framework
    super->constructor( |{ sy-repid }| ).
    try.
        create object mo_model.
        " set parameters of the controller for the GUI interface
        set_gui_interface(
          exporting
             iv_gui_status = 'MAIN'     " IV_GUI_STATUS
             iv_titlebar   = 'MAIN'
        ).
      catch cx_static_check.
        leave program.
    endtry.
  endmethod.                    "constructor

  method pbo_main.
    " pbo_main is for screen 0001, NOT for the selection screen
    try.
        get_con( cv_con_alv_out )->refresh( ).
        rv_res = super->pbo_main( ).
      catch cx_static_check.
        " error handling here
    endtry.
  endmethod.

  method pai_main.
    case iv_ucomm.
      when 'EXIT' or 'BACK' or 'CANC'.
        set screen 0.
        leave screen.
    endcase.

  endmethod.                    "pai_main

  method create_con_dynpro.
    case sy-dynnr.
      when '1000'.
        create object ro_res
          type lcl_con_selscr_1000
          exporting
            io_model      = mo_model
            io_con_main   = me
            is_selections = ms_selscreen. " propagate the selection structure
        " store the controller object
    endcase.

  endmethod.                    "get_con_dynpro

  method create_one_controller.
    " create each output controller on request
    case iv_type.
      when cv_con_alv_out.
        create object ro_res
          type (iv_type)
          exporting
            io_container = new cl_gui_docking_container(
                                  extension = 9999
                                )
            io_main      = me
            io_model     = mo_model.
      when cv_con_list.
        " special case list: no container needed
        create object ro_res
          type (iv_type)
          exporting
            io_main      = me
            io_model     = mo_model.
    endcase.
  endmethod.                    "create_one_controller

  method get_selections.
    " the only access to the global fields in the selection screen
    ms_selscreen-s_carrid   = s_carrid[].
    ms_selscreen-s_connid   = s_connid[].
    ms_selscreen-s_fldate   = s_fldate[].
    ms_selscreen-p_batch    = p_batch.
    if es_data is supplied.
      es_data = ms_selscreen.
    endif.
  endmethod.

  method run_program.

    try.
        mo_model->read_db( ms_selscreen ).
        " decide how to present the data
        if sy-batch = abap_true or
           ms_selscreen-p_batch = abap_true.
          " a list is used
          get_con( cv_con_list )->refresh( ).
        else.
          " an interactive grid is used
          call screen 1.
        endif.
      catch cx_static_check.
        " error handling here
    endtry.

  endmethod.

endclass.

class lcl_con_selscr_1000 implementation.

  method constructor.
    data: lt_fname                  type lvc_t_fnam.

    " 1. call the general constructor with the structure name
    " 2. set the model object
    super->constructor( is_selections = is_selections
                        io_con_main = io_con_main ).
    mo_model = io_model.
    " concrete main controller from generic via casting
    mo_main ?= mo_con_main.

  endmethod.                    "constructor

  method pai_field_change.

*    try.
*        mo_model->perform_field_check( iv_fieldname = iv_fieldname
*                                       iv_source = iv_source ).
*      catch zcx_mvcfw_error into data(lo_err).
*        message lo_err type 'S' display like 'E'.
*    endtry.
  endmethod.                    "pai_field_change

  method run.
    " pass the control to the main controller
    mo_main->run_program( ).
  endmethod.                    "pbo

endclass.                    "create_con_dynpro

class lcl_con_alv_out implementation.
  method constructor.
    super->constructor(
      io_container = io_container
      iv_structure_name = 'sflight' ).

    mo_model = io_model.
    mo_main = io_main..
  endmethod.                    "constructor

*
  method prepare_exclude.
*    Example
*    append:
*      cl_gui_alv_grid=>mc_fc_graph             to et_tab.

  endmethod.                    "prepare_exclude

  method prepare_layout.
    rs_res-cwidth_opt = abap_true.
    rs_res-grid_title = 'Flights table'.
  endmethod.                    "prepare_layout

  method on_double_click.

    field-symbols: <ls_list> type sflight,
                   <lt_tab>  type standard table.

    try.
        assign mr_table->* to <lt_tab>.
        read table <lt_tab> assigning <ls_list> index es_row_no-row_id.
        if sy-subrc = 0 .
          " include double click functions
        endif.
      catch zcx_mvcfw_error into data(lo_err).
        message lo_err type 'S' display like 'E'.
    endtry.

  endmethod.                    "on_double_click

  method refresh.

    field-symbols: <lt_table>       type standard table.
    data: ls_layout                 type lvc_s_layo.

    assign mr_table->* to <lt_table>.

    mo_model->get_out_tab( importing et_out = <lt_table> ).
    refresh_table( ).

  endmethod.                    "refresh
*
  method on_toolbar.
    data: ls_toolbar  type stb_button.

    "    create user buttons
    ls_toolbar-function = 'CLOSE'.
    ls_toolbar-icon = icon_close.
    append ls_toolbar to e_object->mt_toolbar.

  endmethod.                    "on_toolbar

  method on_ucomm.

    data: lv_line_index             type i.

    try.
        case e_ucomm.
          when 'CLOSE'.
            hide( ).
        endcase.
      catch zcx_mvcfw_error into data(lo_err).
        message lo_err type 'S' display like 'E'.
    endtry.
  endmethod.                    "on_ucomm_equi

endclass.                    "lcl_con_alv_out IMPLEMENTATION

class lcl_con_list implementation.

  method constructor.
    super->constructor( ).
    mo_model = io_model.
    mo_main = io_main.
  endmethod.

*--------------------------------------------------------------------*
* Redefinition: create a abap list (used for batch run)
*--------------------------------------------------------------------*
  method refresh.
    data lo_salv type ref to cl_salv_table.

    mo_model->get_out_tab( importing et_out = mt_out ).
    cl_salv_table=>factory(
      exporting
         list_display   = if_salv_c_bool_sap=>true    " ALV Displayed in List Mode
       importing
         r_salv_table   = lo_salv    " Basis Class Simple ALV Tables
      changing
        t_table        = mt_out
    ).
    lo_salv->get_columns( )->set_optimize( ).
    lo_salv->display( ).

  endmethod.

endclass.

module pbo_0001 output.
  go_main->pbo_main( ).
endmodule.

module pai_0001 input.
  go_main->pai_main( iv_ucomm = gv_okcode ).
endmodule.
****************************************************************																																
*   THIS FILE IS GENERATED BY THE SCREEN PAINTER.              *																																
*   NEVER CHANGE IT MANUALLY, PLEASE !                         *																																
****************************************************************																																
%_DYNPRO																																
ZP_MVCFW_REPORT_DEMO																																
0001																																
740																																
             40																																
%_HEADER																																
ZP_MVCFW_REPORT_DEMO                    0001 0001      0  0192 37  0  0200255  0G E                              20160804115046																																
%_DESCRIPTION																																
Main screen																																
%_FIELDS																																
GV_OKCODE		CHAR	 20	80	10	00	00	00	255	  1	O	  0	  0	  0		  0					  0	  0								____________________		
%_FLOWLOGIC																																
process before output.																																
" empty dummy screen that carries a docking container																																
  module pbo_0001.																																
																																
*																																
process after input.																																
  module pai_0001 at exit-command.																																
  module pai_0001.																																
%_PARAMS																																
																																
class ZCL_MVCFW_CON_SELSCR definition
  public
  inheriting from ZCL_MVCFW_CON_DYNPRO
  abstract
  create public .

public section.

*"* public components of class ZCL_MVCFW_CON_SELSCR
*"* do not include other source files here!!!
  methods RUN
  abstract
    raising
      CX_STATIC_CHECK .
  methods CONSTRUCTOR
    importing
      !IV_STRUC_NAME type TABNAME optional
      !IS_SELECTIONS type ANY optional
      !IO_CON_MAIN type ref to ZCL_MVCFW_CON_MAIN optional
    preferred parameter IV_STRUC_NAME .

  methods PAI
    redefinition .
  protected section.
*"* protected components of class ZCL_MVCFW_CON_SELSCR
*"* do not include other source files here!!!
  private section.
*"* private components of class ZCL_MVCFW_CON_SELSCR
*"* do not include other source files here!!!
ENDCLASS.



CLASS ZCL_MVCFW_CON_SELSCR IMPLEMENTATION.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_MVCFW_CON_SELSCR->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_STRUC_NAME                  TYPE        TABNAME(optional)
* | [--->] IS_SELECTIONS                  TYPE        ANY(optional)
* | [--->] IO_CON_MAIN                    TYPE REF TO ZCL_MVCFW_CON_MAIN(optional)
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method CONSTRUCTOR.

    super->constructor(
        iv_struc_name       = iv_struc_name
        io_con_main         = io_con_main
         ).

    if iv_struc_name is initial.
      if is_selections is supplied.
        data(lo_structdescr) = cast cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( is_selections ) ).
        loop at lo_structdescr->components into data(ls_struc_line).
          append INITIAL LINE TO mt_field_list ASSIGNING FIELD-SYMBOL(<ls_field_list>).
          <ls_field_list>-fieldname = ls_struc_line-name.
        endloop.

        create data mr_screen_data like is_selections.
      endif.
    endif.
  endmethod.


* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_MVCFW_CON_SELSCR->PAI
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_UCOMM                       TYPE        SYUCOMM(optional)
* | [<-->] C_DATA                         TYPE        ANY(optional)
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method PAI.
    field-symbols <ls_screen_local> type any.
    data: lr_screen_local type ref to data.

    assign mr_screen_data->* to field-symbol(<ls_screen>).


    if mo_con_main is bound.
      create data lr_screen_local like <ls_screen>.
      assign lr_screen_local->* to <ls_screen_local>.

      mo_con_main->get_selections(
        importing es_data = <ls_screen_local> ).

      super->pai(
        exporting iv_ucomm = iv_ucomm
        changing c_data = <ls_screen_local> ).
    else.
      super->pai(
        exporting iv_ucomm = iv_ucomm
        changing c_data = c_data ).

    endif.


  endmethod.
ENDCLASS.

Assigned Tags

      10 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Hey Jörg,

      i liked your part one of the framework series so much, i had to try this one as well. I'm afraid the report in the text file ZP_MVCFW_REPORT_DEMO got mixed up with the the ZP_MVCFW_ALV_DEMO one from the first part.

      At least i couldn't find any code passage regarding the selection screen class in the text file. I'm really sorry for disturbing you with this minor flaw again, but this topic definately got me hooked.

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      You're absolutely right! I changed the file now. Sorry for the confusion.

      Author's profile photo Tibor Balaz
      Tibor Balaz

      Hey Jörg, uploaded files seem to be lost. Can you add codings on blog post like you did in part 1 and part 3?

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Hello Tibor,

      I inserted the coding into the main post.

       

      Author's profile photo Tibor Balaz
      Tibor Balaz

      Thank you for fast response! I think the code got mixed up as in comment from Former Member. Can you please check it out?

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Gosh - again.... now the correct sources are in. Sorry.

      Author's profile photo John Nijburg
      John Nijburg

      Hi Jörg,

       

      just trying to understand your Freamework and experimenting with it. When i have a Selection-Screen, ALV-Output and at the ALV some Funktions that will show a Popup to enter Data and some Buttons for actions. How should i implement the Popup in that case? I have created a new class thats inherreting from the Subdynpro class. But where should i call this Popup? Or should i first create a new abstract class as sub-Class of Dynpro to use Popups with your Framework?

       

      Best regards

      John Nijburg

       

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Hello John,

      MVC (model view controller) framework for ABAP: Part 3 shows also how to implement a popup dynpro using the framework. Just search for "popup" in there.

       

      Author's profile photo Ingolf Stemmle
      Ingolf Stemmle

      Hello Jörg,

      Looking for a MVC approach for ABAP, I found your nice MVC framework. I immediately started implementing your demos for investigating how it works.

      Since I had to adjust the MVCFW classes to our naming convention, I had to go through the variable declarations as well. As you stated in part 1 you name your classes like CON_MAIN, CON_DYNPRO, CON_SUBDYN, etc. Therefor I thought it would be likely helpful to name the variables always the same way and I changed mo_main into mo_con_main in ZP_MVCFW_ALV_DEMO. (I’d prefer to see clearly that this is a controller and not any common MAIN method of a class.) But then I got an error message, stating that this variable has already been declared elsewhere. I found it as protected attribute in ZCL_MCFW_CON_DYNPRO, but no usage of it. Later on, I recognized, that you use it in ZCL_MVCFW_CON_SELSCR and that it is referenced directly in the constructor of lcl_con_selscr_1000 in ZP_MVCFW_REPORT_DEMO:

      mo_main ?= mo_con_main.

      To make a long story short. Is there any deeper reason for this? I guess it would be more consequent to change this statement to

      mo_main ?= lcl_con_main=>get_instance( ).

      By doing so you could define mo_con_main just as private attribute in ZCL_MVCFW_CON_SELSCR where you only really use it. Of course, constructor parameters and setting mo_con_main has to be adjusted accordingly. At the end of the day all local main controller objects could be defined in the same way. Would you see any problems with this?

      Kind regards

      Ingolf Stemmle

      Author's profile photo Jörg Krause
      Jörg Krause
      Blog Post Author

      Hello Ingolf,

      you're absolutely right - there is no need to have mo_con_main as a protected attribute. The reason why I decided to keep a ref to the main controller in this class is the mechanism that transfers the selection screen parameters to the classes. In fact, mo_con_main could be also moved to the ...CON_SELSCR class because it's only used there.

      However, this framework is in heavy use in my company, and it is growing. Maybe I will post an update blog soon...

      Best regards

      Jörg