Skip to Content

SALV and PEPPER : Editing individual columns in the SALV

SALV and Pepper – Edit it Real Good


Editing Specific Columns in CL_SALV_TABLE

One Sentence Summary

How to write a report using CL_SALV_TABLE and have specific columns open for editing.

Back Story

SCN Blogger Naimesh Patel has written several articles about custom workarounds to make CL_SALV_TABLE editable, for example the following:-

The development department at SAP are horrified that developers around the world are trying to make the SALV editable, because it is naughty and against the rules. Nonetheless it is a requirement every developer in every company has and lots of such developers just don’t care that it’s naughty.

I have declared February the 8th “International Editable SALV Day” on the grounds that was when people first started begging SAP to add this functionality – in 2008.

I imagine I will be writing another blog on the same day in 2016 to celebrate the 8th anniversary of nothing changing.

The irony is at the moment there is a push for companies to adopt the non-on-premise version of S/4 HANA (though none have to date) on the grounds that although there will be far fewer user exits SAP will quickly add missing functionality when you ask for it.

Can anyone see the gap between that promise and the SALV situation?

Column the Barbarian

Anyway, time to stop moaning and move on to the problem at hand. The original solution by Naimesh made the CL_SALV_TABLE editable, but you had to open it in read only mode and then press a button to change to editable mode.

So I asked him if there was a way that you could open up the SALV directly in editable mode, like you can with CL_GUI_ALV_GRID. Every less button pressed by the users is a good thing. He came back with a solution almost at once – it is described in the blog accessed by the link at the top of this blog.

So far so good, but the whole grid was editable – and 99 times out of a hundred you only want one or two columns editable. So I thought it was my turn to contribute something in this area, so I worked out a way to open the SALV in editable mode with only one or two columns changeable.

This may not be the best solution in the world, but it works. In fact what I am going to write about here is about the fourth iteration already, but it suddenly occurred to me I could spend the rest of my life fiddling around trying to get this “perfect” and then this blog would never get written.

MVC Potter and the Philosophers Corner

One area I spent ages agonising over was that given an MVC design what class should do the work of altering the data table so that some columns were editable? It is the models job to know what data a user is allowed to change, and yet the actual mechanism by which you change the data table is very much UI technology specific, and thus the province of the view.

Many people have told me I should not be mixing up philosophical talk – matters of OO design and the like – with the actual instructions on how to do something, but to me they are two sides of the same coin. Right at the moment I am reading one of the “Head First” books about software design and they are presenting how a bad design can really stuff you up down the track even if the program works technically.

One area I still have not finished is to isolate all the code that would be exactly the same in every report into dedicated classes. I am fanatic about this matter – the idea is that if you find yourself with the same task you had six months ago, say creating a pop up box showing a data table, then if you find yourself cut and pasting large chunks of code from the last program and then only changing 5% of it, then something is rotten in the state of Denmark. This is especially true of the SALV class where you might find yourself making twenty lines of data declarations for all the helper objects each time.

My other aim is to have a report framework which is not dependant on the actual UI technology i.e. CL_SALV_TABLE. As an experiment I want to be able to switch between CL_SALV_TABLE and CL_GUI_ALV_GRID by changing just one line in the calling program – or use a configuration table and thus have to change no lines. That way if a successor to the SALV comes along and I think it is much better than I do not have to change half a million report programs.

To summarise – a “perfect” design would be one where the vast bulk of the code is 100% specific to the report at hand and the small proportion remaining consisting of calls to various generic helper classes – thus separating the “things that change from the things that stay the same”. I have not quite got there yet, but as they say, an aim in life is the only treasure worth finding.

Nelson’s Editable Column

In the code that follows you will see that model is shouting out what columns are editable or need to be renamed or allow a drill down etc. and then the view can obey those instructions using its specific UI technology, which in this case is going to be the SALV.

How I am going to do this is by stepping through the program flow so you can see what happens in the same way you would if you filled the custom methods with break points and executed the report.

At the end of the blog will be a good old SAPLINK file so you can install the objects in your own system and play with them.

Bogus Syntax Error

First off I want to mention an annoying problem I have and then people can tell me how to get around it – it is either something obvious, or I am heading down the right path and just need to make things more generic.

The “monster monitor” program is a type one executable report with a selection screen. As we know the selection parameters are global variables (Oh no!). If I have my local class definitions and implementations in the same program as the selection screen then such classes have access to those selection criteria and can use them for database access for example.

However if the class definitions are in one INCLUDE and the class implementations are in another INCLUDE then although the classes still have access to the selection screen parameters and the program will still run, if you do a syntax check whilst in the INCLUDE with the implementations then you get a false syntax error saying something like “variable P_WHATEVER is unknown”.

This makes the syntax check useless and since I do one every ten seconds on average I had to get around this somehow. One way would be not to use INCLUDES though I thought the consensus was that INCLUDES for use with a single program were OK.

What I did was to create a local class for the sole purpose of storing the selection screen parameters, and the local classes that needed any of those values would never access the global parameter variables directly, thus stopping the bogus error message.

“This nonsense is the only way I can avoid getting bogus syntax
“errors when doing a syntax check on the local class implementations
CREATE OBJECT go_selections
= s_numbr[]
= s_name[]
= p_vari
= p_edit
= p_macro
= p_send
= p_email.

All the selections class does is to take in the values the user entered on the screen and place them in identically named public attributes so they can be used by other local classes. This is of course a right royal pain. If there is not an obvious solution to the bogus error message I imagine the next step would be some sort of generic class where you could add each selection option to a sort of COSEL table and each parameters to a list of value pairs. There might even be such a class available in SAP standard.

Anyway there is one local class called LCL_APPLICATION with a single MAIN static method which is called directly after the selection options have been stored. So there are only two commands after the START-OF_SELECTIONS, creating the selection options and then LCL_APPLICATION=>MAIN( ).

Lion’s MAIN

This is a method that is crying out to be made generic, as the vast bulk of the code would be exactly the same in every report.


  METHOD main.
* Local Variables
DATA: ld_report_name TYPE string,
TYPE syrepid.

    CONCATENATE sytcode sytitle INTO ld_report_name

    CREATE OBJECT mo_model.
CREATE OBJECT mo_view TYPE zcl_bc_view_salv_table.
CREATE OBJECT mo_controller
= mo_model
= mo_view.

    mo_model->data_retrieval( ).
->prepare_data_for_ouput( ).

    “It is bad news to pass system variables as parameters
= syrepid.

In the above code the only thing I might want to vary is the TYPE of the view object being created, so I could pass this in as a parameter. Next, if we are online we want to automatically create a screen with a container so we can do funky tricks like adding our own commands programmatically or enabling editing mode, neither of which you can do in full screen mode.

Naturally if we are running a batch job there is no user to invoke the custom commands or edit the data.

In the below code the model never has direct contact with the view but the things that the model wants to say e.g. which fields are editable get passed on regardless. In a simple program like this, the controller and the application class are pretty much the same thing, I only split them out in case you want to go bananas and have the program call lots of different views for some reason.

  IF sybatch IS INITIAL.
* Calling a SALV report whilst creating a container
* automatically
* Program flow is as follows:-
* –> INITIALISE (Generic)
* –> Application Specific Changes (in this program)
* –> Display (Generic)

= ld_report_name
= ld_repid      ” Calling program
= go_selections->p_vari
= go_selections->p_edit
= mo_model->md_edit_control_field
= mo_model->mt_editable_fields
= mo_model->mt_technicals
= mo_model->mt_hidden
= mo_model->mt_hotspots
= mo_model->mt_subtotal_fields
= mo_model->mt_field_texts
= mo_model->mt_user_commands
= mo_model->mt_output_data ).

* If this is running in the background there is no way
* in the world we want/need a container, as there is no
* chance for the user to press any user command buttons or
* edit the data, as there is no user, and no screen for the
* container to live on for that matter
= ld_repid
= go_selections->p_vari
= mo_model->mt_technicals
= mo_model->mt_hidden
= mo_model->mt_subtotal_fields
= mo_model->mt_field_texts
= mo_model->mt_user_commands
= mo_model->mt_output_data ).
ENDIF.“Are we running in the background?

    IF go_selections->p_email IS NOT INITIAL.
->send_email( ).

  ENDMETHOD.                                              “main

ENDCLASS.                    “lcl_application IMPLEMENTATION

In the above code the program flow was detailed very precisely. I am using the same technique SAP itself uses to avoid having to create a screen explicitly and I have talked about this in blogs as well as the book.

In this case, of course we are going to be editing the data so we need a container. A full screen SALV grid has limited functionality e.g. you cannot add commands programmatically to the toolbar, so we virtually always want a container, but having to create the screen and paint a container on it for each report is mundane work which we want to automate.

Going back to the method calls to the “create container” method all those IMPORTING parameter tables like editable fields and hotspots and the like are all optional as not every report will want to take advantage of all of these features, though most will want one or two and of course as time goes by the users will ask for extra things.

The next bunch of code moves all the provided parameters to the equivalent instance variables in the view class, and then creates a screen by the only means available to a method in a class i.e. it calls a function module. There is a standard function module that SAP uses in its own programs for this purpose, but for reasons best known to themselves the created screen has a big hole in it, so I created a Z copy minus the hole.

METHOD zif_bc_alv_report_view~create_container_prep_display.
* Creating a Container Automatically
* The below function creates a screen and a container, and then does
* a callback to method FILL CONTAINER CONTENT of interface
* IF_SALV_CSQT_CONTENT_MANAGER so that the calling class must
* implement method FILL_CONTAINER_CONTENT
* This way for CL_SALV_TABLE we can add our own functions without having
* to create a PF-STATUS
= id_report_name.
= id_edit_control_field.
= if_start_in_edit_mode.
report    = id_report_name.
variant    = id_variant.
= it_editable_fields[].
= it_technicals[].
= it_hidden[].
= it_hotspots[].
= it_subtotal_fields[].
= it_field_texts[].
= it_user_commands[].

  CREATE DATA mt_data_table LIKE ct_data_table.
GET REFERENCE OF ct_data_table INTO mt_data_table.

= me
title            = id_title.


I just can’t Container myself

For 15 years SAP has been pushing people to use OO programming, but in all that time they have not found a replacement for the CALL SCREEN method for bringing up a user interface, something you cannot do from within the OO framework.

So the recommendation is to use function modules for UI processing as you can do a CALL SCREEN from within a function module. The function module in the code above contains a screen definition, just a screen filled with a big container. The function module creates the container and calls up the screen. In the PBO processing of that screen control is passed back to the calling program.

The calling class (the view class) implements an interface which has the method “fill container content” a method which expects a container object to be supplied. The code below is a copy of the standard SAP code in the PBO section of the function modules screen. When a call is made to the “fill container content” control is returned to our view class.

As a PBO is called every time the user interacts with the screen the code has to make sure the container is created only once.

FORM pbo.

  SET PF-STATUS ‘D0100’.

  IF gr_container IS INITIAL.
IF cl_salv_table=>is_offline( ) EQ if_salv_c_bool_sap=>false.
CREATE OBJECT gr_container


= gr_container ).

ENDFORM.                    “pbo

Once the screen is running the CL_SALV_TABLE is going to handle all the user interaction, the screen is just looping through PBO/PAI until such time as the user presses a CANCEL or BACK or EXIT button to shut down the screen.

Next we will back in the generic section of ZCL_BC_VIEW_SALV_TABLE i.e. in a method which will be exactly the same for every single report and thus does not need to be redefined.

All the code below does is make a call to PREPARE_DISPLAY_DATA. You will have seen in the code above that when we are in the background the PREPARE_DISPLAY_DATA method gets called directly, without having to fluff about creating a screen and container, as there is no user to see the screen.

METHOD if_salv_csqt_content_manager~fill_container_content.
* This gets called from function SALV_CSQT_CREATE_CONTAINER PBO module
* which creates a screen and a container, and passes us that container
* in the form of importing parameter R_CONTAINER
* Local Variables

    ASSIGN mt_data_table->* TO <lt_data_table>.

= md_report_name              ” Calling program
= ms_variantvariant          ” Layout
= mf_start_in_edit_mode
= md_edit_control_field
= mt_editable_fields
= mt_technicals
= mt_hidden
= mt_hotspots
= mt_subtotal_fields
= mt_field_texts
= r_container 
= mt_user_commands ” Toolbar Buttons
= <lt_data_table> ).” Data Table


This might all seem ludicrously complicated, but bear in mind that the code is only written once, and then you never have to bother with it every again, it just gets re-used in every subsequent report. Anyway, the next method that gets executed is in three parts – a generic part which sets up the basic settings for CL_SALV_TABLE, a call to an application specific method which alters the columns in the SALV grid based on the instructions the model class issued, and lastly a simple call to a method to call up the SALV grid.

METHOD zif_bc_alv_report_view~prepare_display_data.
* Step One – Set up the Basic Report
= id_report_name
= id_variant                      ” Layout
= if_start_in_edit_mode
= id_edit_control_field
= it_editable_fields
= io_container 
= it_user_commands
= ct_data_table ).

* Step Two – makes changes based on tables sent in by the model
= it_technicals
= it_hidden
= it_hotspots
= it_subtotal_fields
= it_field_texts ).

* Step Three- Actually Display the Report
( ).


In the next method called we first of all use the factory method to get an instance of CL_SALV_TABLE linked to the container on the screen our lovely function module called up.

We then add the basic toolbar, followed by any custom report specific commands our model class has said it can respond to. This is a good time to set up the MO_COLUMNS object as alter on we will need this to change attributes of various report columns, like making them hotspots or whatever.

We set some handlers for when a user double clicks on a cell in the grid, or presses a n icon in the toolbar. Both actions will cause the CL_SALV_TABLE to raise an event which our custom class needs to handle (in fact all our class does is raise a corresponding event of it’s own for the controller to respond to).

  METHOD zif_bc_alv_report_view~initialise.

* If we have a container, then we can add our own user defined
* commands programtaically
*——————————————————————–          cl_salv_table=>factory(
= io_container
= mo_alv_grid
= ct_data_table[] ).

          display_basic_toolbar( ).
IF it_user_commands[] IS NOT INITIAL.
( it_user_commands ).

        mo_columns = mo_alv_grid->get_columns( ).
( id_variant ).
( ).

At long last, this is where things start getting interesting i.e. we are going to set things up so that certain columns are editable i.e. the purported purpose of this entire blog.

In the blogs I linked to at the start the subject of creating a custom class ZCL_SALV_MODEL was discussed, this class has one purpose in life, and that is to get hold of the underlying CL_GUI_ALV_GRID instance which lives hidden like Rapunzel in the tower that is CL_SALV_TABLE. This is a subclass of CL_SALV_MODEL_LIST. As we shall see later it has a method for climbing up the tower and rescuing the princess from the evil Rumpelstiltskin who is head of ABAP development at SAP. Or getting the underlying grid object – it’s one or the other; I can’t remember which offhand.

There is another method to the class called “set editable”. This was the miracle solution discovered by Naimesh whereby our custom code can respond to the event raised when the SALV object is ready to burst onto our screen, so we can hold it up for a second and make it editable.

We will get into that code in a minute, and discuss the slight additions I have made to it, and afterwards discuss the other method call in the code below, which changes the data table such that certain columns can be edited. To make a column editable in the SALV (or indeed in CL_GUI_ALV_GRID) both the data in the table and the field catalogue have to be fiddled with.

DATA: lo_salv_model TYPE REF TO cl_salv_model.

        “Narrow casting
“CL_SALV_MODEL is a superclass of CL_SALV_TABLE
        lo_salv_model ?= mo_alv_grid

        “Object to access underlying CL_GUI_ALV_GRID
CREATE OBJECT mo_salv_model
= lo_salv_model.

        IF if_start_in_edit_mode = abap_true.
“Prepare the Field Catalogue to be Editable

          io_salv              = mo_alv_grid
= id_edit_control_field
= it_editable_fields ).
“Prepare the Data Table to be Editable
EXPORTING id_edit_control_field = id_edit_control_field
= it_editable_fields
CHANGING  ct_data_table        = ct_data_table ).

      CATCH cx_salv_msg.
MESSAGE ‘Report in Trouble’ TYPE ‘E’.

  ENDMETHOD.                    “zif_bc_alv_report_view~initialise

It is time to introduce yet another custom class ZCL_BC_SALV_EVENT_HANDLER. This class has the job of responding to the event raised when the SALV has finished building itself and is ready to be displayed (the REFRESH event). The code below I copied from the prior blog, all I added was some lines to store a table of what fields (columns) we want to make editable, and what the name of the control field in the data table is. A control field is one that has the structure LVC_T_STYL. I usually call such a field CELLTAB but I could call it FRUIT_LOOPS if I wanted, so I don’t want to hard code the field name, instead the model declares what such a field is named. The table of editable fields and the control field will be used later when the “refresh” event of the SALV is called.

METHOD set_editable.
* Local Variables
DATA: lo_event_handler TYPE REF TO zcl_bc_salv_event_handler.

  “Ensure one, and only one, static event handler exists
IF zcl_salv_model=>mo_event_handler IS NOT BOUND.
CREATE OBJECT zcl_salv_model=>mo_event_handler
TYPE zcl_bc_salv_event_handler.

  lo_event_handler ?= zcl_salv_model=>mo_event_handler.

  lo_event_handler->md_edit_control_field = id_edit_control_field.
->mt_editable_fields    = it_editable_fields.

  APPEND io_salv TO lo_event_handler->mt_salv.

  “At such time as any SALV object is displayed, call the
“after refresh event to make the grid editable
SET HANDLER lo_event_handler->on_after_refresh

  “Sometimes the icons needed for an editable grid do not
“display, so we have to force the issue
IF io_salv->get_display_object( ) = 3.
SET HANDLER lo_event_handler->on_toolbar


Now we come to a method in ZCL_BC_VIEW_SALV_TABLE called “make column editable”. This takes in the data table from the model and changes the control field in each row of the data table so that our desired columns are made ready to be editable. You will see I have not quite finished it yet as there are comments saying what I still need to do. As mentioned at the start of the blog I was going to wait till I was 100% ready, but that would mean this blog never be written, as I am never 100% satisfied with the code I write.

METHOD make_column_editable.
* Local Variables
DATA :ls_celltab      TYPE lvc_s_styl,
TYPE lvc_t_styl,
TYPE sytabix,
LIKE LINE OF it_editable_fields.

  FIELD-SYMBOLS: <ls_data_table> TYPE any,
TYPE lvc_s_styl,
TYPE lvc_t_styl.

* Dynamically create work area for looping through the table
* that was passed in
CREATE DATA ldo_table_line LIKE LINE OF ct_data_table.

  ASSIGN ldo_table_line->TO <ls_data_table>.

  LOOP AT ct_data_table ASSIGNING <ls_data_table>.

    “Need a TRY/CATCH block here – “if the control field is not of type LVC_T_STYL
“then a system generated exception will be thrown
ASSIGN COMPONENT id_edit_control_field OF STRUCTURE <ls_data_table>

    TO <lt_celltab>.

    IF sysubrc <> 0.
“We cannot go on, the control field is not in the structure
“Need a fatal error here, violated pre-condition

    LOOP AT it_editable_fields INTO ld_editable_field.

      READ TABLE <lt_celltab> ASSIGNING <ls_celltab>

      WITH KEY fieldname = ld_editable_field.

      IF sysubrc <> 0.
= sytabix.
fieldname = ld_editable_field.
INSERT ls_celltab INTO <lt_celltab> INDEX ld_index.
READ TABLE <lt_celltab> ASSIGNING <ls_celltab>

        WITH KEY fieldname = ld_editable_field.

      IF <ls_celltab>style EQ cl_gui_alv_grid=>mc_style_enabled.
style = cl_gui_alv_grid=>mc_style_disabled.“Read Only
style = cl_gui_alv_grid=>mc_style_enabled.“Editable

    ENDLOOP.“List of Editable Fields
ENDLOOP.“Lines of the Data Table

ENDMETHOD.”Make Column Editable

Originally I had the “application specific changes” method redefined in every calling report, but I have changed this design to instead have the model class saying what fields it wants changed e.g. length changed, turned into a hotspot, description changed etc.

A certain German someone will say my model class is the devil incarnate and that I certainly shouldn’t be using it to say what the column headings should be. Well you know what? As Chas and Dave would say “I don’t care. I don’t care, I don’t care, I don’t care if he comes round here, I’ve got my model class on the sideboard here, let your mother sort it out if he comes round here.”

METHOD zif_bc_alv_report_view~application_specific_changes.
* The job of the model is to say waht fields can be drilled into, and what
* alternative names they have etc…
* The job of the view is to realise this technically
* Since this is CL_SALV_TABLE we cannot make fields editable here, but
* we can do all the other adjustments needed
* Local Variables
DATA: lo_error          TYPE REF TO cx_salv_msg,
TYPE REF TO cx_salv_data_error,
TYPE REF TO cx_salv_not_found,
TYPE bal_s_msg,
TYPE abap_bool,
TYPE lvc_fname,
TYPE zsbc_alv_texts.

IF if_optimise_column_widths = abap_true.
( ).

* Technical Fields
LOOP AT it_technicals INTO ld_field_name.
( id_field_name  = ld_field_name
= abap_true ).
* Hidden Fields
LOOP AT it_hidden INTO ld_field_name.
( id_field_name  = ld_field_name
= abap_false ).
* Hotspots
LOOP AT it_hotspots INTO ld_field_name.
( id_field_name  = ld_field_name
= abap_true ).
* Renamed Fields / Tooltips
LOOP AT it_field_texts INTO ls_alv_texts.
IF ls_alv_textstooltip IS NOT INITIAL.
( id_field_name = ls_alv_textsfield_name
= ls_alv_textstooltip ).
IF ls_alv_textslong_text IS NOT INITIAL.
( id_field_name = ls_alv_textsfield_name
= ls_alv_textslong_text ).
IF ls_alv_textsmedium_text IS NOT INITIAL.
( id_field_name  = ls_alv_textsfield_name
= ls_alv_textsmedium_text ).
IF ls_alv_textsshort_text IS NOT INITIAL.
( id_field_name = ls_alv_textsfield_name
= ls_alv_textsshort_text ).
* Subtotals
LOOP AT it_subtotals INTO ld_field_name.
( id_field_name  = ld_field_name
= abap_true ).

    CATCH cx_salv_not_found INTO lo_not_found.
= abap_true.
“Object = Column
“Key    = Field Name e.g. VBELN
=>require( that            = |{ lo_not_found->object } { lo_not_found->key } must exist|
= boolc( lf_error_occurred = abap_false ) ).
CATCH cx_salv_data_error INTO lo_data_error.
= lo_data_error->get_message( ).
MESSAGE ID ls_errormsgid TYPE ‘E’ NUMBER ls_errormsgno
WITH ls_errormsgv1 ls_errormsgv2
msgv3 ls_errormsgv4.
CATCH cx_salv_msg INTO lo_error.
= lo_error->get_message( ).
MESSAGE ID ls_errormsgid TYPE ‘E’ NUMBER ls_errormsgno
WITH ls_errormsgv1 ls_errormsgv2
msgv3 ls_errormsgv4.

ENDMETHOD.”Application Specific Changes

You will notice a nice lot of error handling at the end, errors raised here indicate a serious bug in the calling program –i.e. trying to manipulate a field which does not exist – and so should stop processing dead until the bug is corrected.

I am using the “design by contract” class here…

… to make it crystal clear that the calling program should not be trying to change a field that is not in the output structure. If it does then the program will not run until such time as the bug is fixed.

The “display” method in ZCL_BC_VIEW_SALV_TABLE just calls the “display” method of CL_SALV_TABLE. If we were using a different UI technology maybe the “display” method would have to be more complicated. In any event the time has come, the Walrus said, to actually display the SALV grid on the screen.

At some point in the bowels of the CL_SALV_TABLE “display” method the event “on after refresh” is raised (this is an event of the CL_GUI_ALV_GRID that lives inside the SALV) and our custom event handler class has been set up to intercept that event.

The On After Refresh Prince of Bel Air

Let us have a sticky beak at the two methods in the ZCL_BC_SALV_EVENT_HANDLER class. First off we shall look at the ON_AFTER_REFRESH method. During creation the event handler class was (optionally) passed a list of editable fields and the name of the edit control field. If that information was not passed in at the time of creation, the code below will just make the whole grid editable.

If a list of editable fields was passed in the field catalogue is modified to make the desired fields editable. We could not do this earlier as the CL_GUI_ALV_GRID hidden inside CL_SALV_TABLE is not instantiated till the DISPLAY method is called.

The instance of the SALV lives inside an internal table of SALV instances called MT_SALV. The import parameter “sender” in the code below refers to the CL_GUI_ALV_GRID instance that sent the “on after refresh” method. Thus you could have a screen with more than one SALV on it, and the correct grid would be processed.

METHOD on_after_refresh.
* What we are doing here is enabling the SALV GRID to open in editable
* mode
* Local Variables
DATA: lo_grid            TYPE REF TO cl_gui_alv_grid.
DATA: ls_layout          TYPE lvc_s_layo,
TYPE lvc_t_fcat,
LIKE LINE OF mt_editable_fields.
DATA: lo_salv            TYPE REF TO cl_salv_table.
DATA: lo_salv_model      TYPE REF TO cl_salv_model,
TYPE REF TO zcl_salv_model.

  FIELD-SYMBOLS: <ls_fcat> LIKE LINE OF lt_fcat.

  TRY .
LOOP AT mt_salv INTO lo_salv.
“Narrow casting
“CL_SALV_MODEL is a superclass of CL_SALV_TABLE
        lo_salv_model ?= lo_salv

        “Object to access underlying CL_GUI_ALV_GRID
CREATE OBJECT lo_sneaky_model
= lo_salv_model.

        lo_grid = lo_sneaky_model->get_alv_grid( ).
CHECK lo_grid EQ sender.

        “Deregister the event handler
“i.e. we do not want to keep calling this every time
“the user refreshes the display.
“Once the report is running the user can control whether
“the grid is editable by using the icons at the top of the screen
SET HANDLER me->on_after_refresh
          ACTIVATION space

        “Set editable
IF md_edit_control_field IS NOT INITIAL.
“Make certain fields editable based on FIELDCAT
stylefname = md_edit_control_field.
->get_frontend_fieldcatalog( IMPORTING et_fieldcatalog = lt_fcat ).
LOOP AT mt_editable_fields INTO ls_editable_fields.
READ TABLE lt_fcat ASSIGNING <ls_fcat>

            WITH KEY fieldname = ls_editable_fields.
IF sysubrc = 0.
edit = abap_true.
->set_frontend_fieldcatalog( lt_fcat ).
“Make everything editable
edit = ‘X’.
->set_frontend_layout( ls_layout ).
->set_ready_for_input( 1 ).
CATCH cx_salv_error.


ENDMETHOD.”On after refresh method of ZCL_BC_SALV_EVENT_HANDLER

Toolbar Bar Bar, Bar Barbara Ann

The next problem – as solved in the blog by Naimesh – is that the SALV does not expect the grid to be editable, so some ICONS in the toolbar you would normally expect for editable grids are missing. This is really only applicable when all the fields are editable, as that is the only situation where copying a row makes sense, as then you would change a key field. If only one field is editable you would not want the user to be able to delete a row either.

The most important thing for me is to get the separators working! There is no point in my regurgitating the code here – you can see it in the other blog, and I have not changed anything, though I am going to add something to only add the extra buttons if we do not have a specific list of editable fields.

Lord Editable Column-Ostomy Bag

Now we see the final result! The grid opens with several columns editable.


List of Ingredients

Here are the custom objects I had to create to get this working. In lots of ways it doesn’t matter how many were needed as apart from the calling report they are all totally generic and can get re-used again and again.

·        Calling Report

·        Interface ZIF_BC_ALV_REPORT_VIEW (UI Technology Agnostic)

·        Class ZCL_BC_VIEW_SALV_TABLE (SALV Specific)

·        Class ZCL_SALV_MODEL (to access the CL_GUI_ALV_GRID)

·        Class ZCL_BC_SALV_EVENT_HANDLER (for opening in edit mode)

·        Function Module ZSALV_CSQT_CREATE_CONTAINER (to create screen)

I will whip up a SAPLINK file and attach it to this blog at some point in the near future.

Going Forward

As I keep stressing this is a work in progress, I am going to have concentrate on my presentation for the SAP Australian Users Group next week (SAUG, pronounced “Sausage”), and then on my presentation for SAP TECHED in Las Vegas in October (boast boast) and then I will be able to work on the next iteration of this.

In the interim I am happy to take questions, or suggestions for improvements.

Cheersy Cheers


10/11/2015 – I have attached the SAPLINK nugget (as a text file, as the SAP community network does not allow SAP specific formats like nuggets) – please see my comments below for some manual steps you will unfortunately have to make after importing the nugget to your system.

You must be Logged on to comment or reply to a post.
  • I apologise about the lack of paragraph breaks in the second half of the blog.

    When I wrote the thing there were gaps between the paragraphs, which vanished when I pressed the “publish” button.

    When I try to edit the blog and add blank lines between the paragraphs, when I press “publish” I just get “an unexpected error occurred”.

  • Sometimes, by way of variety, the error when editing and then saving is “we could not complete your request – please check the form for details”.

    I wonder what “the form” is. In the UK it is a paper dedicated to Horse Racing. I am not sure what the term means in this context. I am not sure the horses could help me out here.

    I thought maybe “the form” was the text of the blog I had just written and “the details” would be some sort of text highlighted because it was causing an error. But I could not see anything.

  • Regardless of the formatting, your blogs are always entertaining and informative. Thanks for sharing your OO insights.

    “…it has a method for climbing up the tower and rescuing the princess from the evil Rumpelstiltskin who is head of ABAP development at SAP…” LOL! 😆

  • However if the class definitions are in one INCLUDE and the class implementations are in another INCLUDE then although the classes still have access to the selection screen parameters and the program will still run, if you do a syntax check whilst in the INCLUDE with the implementations then you get a false syntax error saying something like “variable P_WHATEVER is unknown”.

    I remember something like this and I do remember weird code completion problems when the local class definitions were split from implementations using includes, but now I’m unable to reproduce either of the problems (I’m on SAP_BASIS 702 SP 11). The only way I’m getting unknown variable for selection screen globals used in a local class (definition or implementation) is when the selection screen variable use happens “before” its definition and then the main program of course does not compile as well.

  • hi,Paul, if i just want to make one row’s particular column be editble,not all rows’s column,for example. I want to make the first column of seconde row be editble,but the first column of first row be can i do that?

    • Hello,

      This is quite a comment requirement – for example you may want to let users change the price of sales orders, but not if the order has had any deliveries created against it.

      In such a case for some rows the price column would be editable, for other rows it would be read only.

      In the CL_GUI_ALV_GRID this is a trivial matter – one column of your data table is defined as type LVC_T_STYL (I call such a column CELLTAB in the code above) and this is a table within a table, one table per rows, listing all the columns on that rows which can be edited.

      For CL_SALV_TABLE the contents of the data table would be exactly the same i.e. you would still fill the CELLTAB column of each row with the columns you want to be editable for that row.

      In the example above I have a generic Z class which loops through the data table and changes the columns the model has specified such that they are made editable. You would want to have a local class in your specific application which inherits from the Z view class, and implements the logic you need during the “make column editable” method.

      That is, when looping through the data table, instead of treating each rows exactly the same and making all the required columns editable, the specific columns are only made editable conditionally, based on whatever logic you require.

      Cheersy Cheers


      • Hi,Paul,

        I try it using column CELLTAB ,and make the field CELLTAB table have my editable filed,and style is editble,but it dosn’t work.Follow you way,I can only make entire column to be editable.Can you try it youself?

        • As they say, if you want something done, ask a busy person!

          As it happens I am currently whipping up an editable SALV grid report for my presentation about ABAP Push Channels in Las Vegas at SAP TECHED next month, boast boast, boast boast.

          Anyway, I will set it up so you can alter the monster task description, but only for certain rows, the rows where the monster has not yet carried out the task. I’ll get it working and then publish the details here of how I did it, which I have no doubt will correspond to the approach I outlined above, although you never know.

  • Are you still planning on adding a SAPLINK file? I haven’t been able to replicate this. I’ve only managed to make the whole ALV editable, rather than individual columns. 🙂

    • I will have the SAPLINK file ready by the end of next week – I need this to be ready for my demonstration in Las Vegas at TECHED, and the nugget has to be stand-alone enough to be imported into a new SAP system.

      This week I have been trying to get individual cells editable as per another request in the comments, and it is looking like this is impossible at the moment, though impossible things always break under pressure. I won’t have individual cells problem  fixed anytime soon, but as I have said individual columns are no problem.

      • I’m realising that my problems replicating your stuff stems from you using a bunch of 7.40 stuff that my 7.20 system doesn’t have. Looking forward to the SAPLINK regardless – we’ll upgrade eventually. Would have liked to be there for the talk, but alas. Some day I’ll get to TECHED.

    • I got this working on a 7.02 system, EHP5. The only 7.40 system I have access to is the CAL trial from SAP.

      In fact in my book the vast bulk of the technology I describe works on a 7.02 system, even if some technologies like BRF+ and the Floorplan Manager are much better in a 7.40 system.

      In fact the only topics I discuss which require a system greater than 7.02 are ABAP in Eclipse (needs 7.31 as minimum) and naturally the 7.40 specific ABAP features along with HANA Programming artifacts like CDS Views and ABAP Managed Database Procedures. Push Channels only work in a 7.40 system also.

      Anyway yesterday evening I tested moving my SAPLINK nugget into a blank SAP system to make sure it all worked. Some bits did not come through so I will try again today, but anyway – almost there.

      • Ah alright, I’m confusing some stuff then probably. I’m still figuring out how all these weird events fit together. I don’t have your book yet, but I do have it on the way in the mail. So I’ll get it… in the future.

        Anyway, Thanks for working on it. 🙂

      • I got it working in the end, but it took me quite a bit to figure out how to retrieve the data after it had been input. I solved this by using the get_grid method to retrieve the CL_GUI_ALV_GRID and then calling the CHECK_CHANGED_DATA method. This updates the internal table with the data.

        Edit: This doesn’t preserve lower case of letters. 🙁

        Edit 2: It does if you tell the field catalog to allow it at the same time that you set the edit value:

                     <ls_fcat>edit = abap_true.

                     <ls_fcat>lowercase = abap_true.

        Alternatively, can use the standard SALV methods:

             DATA lr_column TYPE REF TO cl_salv_column_table.

             TRY .

                 lr_column ?= lr_columns->get_column( ‘NOTETEXT’ ).

               CATCH cx_salv_not_found.


             lr_column->set_lowercase( ).

        This seems so obvious now, but I spent hours not figuring it out. The internal table of data seems to also update on occasion without calling check_changed_data. It’s nice having the ability to force it though.

  • I spend a lot of time working out the kinks in this, now that I got the general concept working. One thing I haven’t cracked is that custom layouts remove edit functionality after a refresh. I feel like the on_after_refresh should be triggered and fix it, but the editability is lost in the refresh.

    Here’s how to replicate:

    1. Enable saving of layouts.

       DATA lr_layout TYPE REF TO cl_salv_layout.

       DATA ls_key TYPE salv_s_layout_key.

       ls_keyreport = sycprog.

       lr_layout = lr_alvgrid->get_layout( ).

       lr_layout->set_save_restriction( if_salv_c_layout=>restrict_none ).

       lr_layout->set_default( abap_true ).

       lr_layout->set_key( ls_key ).

    2. Save a (personal) default layout in your ALV grid so it opens for you automatically.

    3. Trigger a refresh. Any refresh_mode, they all mess up the layout.

      lr_alvgrid->refresh( ).

    In the debugger you can see it happen when you get to the IF_SALV_ADAPTER~SET_METADATA method a few layers down. It retrieves a layout and fieldcatalog that doesn’t have the edit fields set properly (in line 62 and 73 respectively on 7.02) and later overrides with them in the same method (line 166 and 168):

      r_grid->set_frontend_layout( is_layout = ls_layo ).

      r_grid->set_frontend_fieldcatalog( it_fieldcatalog = lt_fcat ).

    Maybe I’ll solve this. I haven’t yet. :\

    • I still have not got the ability to edit individual cells working. I will not give up.

      The problem is we are fighting SAP here and they have got more developers than we have.

      We are trying to solve an obvious business need all SAP customers have. SAP development have been given a “rule” that says nobody wants/needs this, so go all out to prevent it.

      I was at TECHED last week and I got the impression that no-one at SAP thinks that anyone is even using anything other than UI5 now, so why bother with anything SAP GUI related?

      100% of the customer base is also on HANA as well, and when ABAP 7.5 came out in some sense last week you can just click your fingers and get it installed in your ERP system.

      • SAP works in mysterious ways.

        I hope you get the individual cells working. I’ve been trying to make dropdowns in an editable ALV field have different values in different rows, but dropdowns seem to be column specific. Much like editability.

  • Hello Paul,

    just a thought which is running around my head since a while: Why to try make SALV act like ALV grid when we can make opposite?

    SALV creates ALV grid directly or call REUSE_ALV_GRID_DISPLAY FM – we can do same think, we can create a class which will do it for us.

    I know that there will be a question why we should reinvent the wheel? My answer is: Because this wheel looks like a square to me…

    Some of the methods from SALV can be reused as they are static, some would need to be developed from the scratch , some standard ALV grid function can be redefined to make it easier and faster to call, but….

    … as you said also many times in here, this needs some time to develop and I’d say the best would be to have a community support and GIT for that purpose.

    What  do you think?



    • Somebody – either in the comments section of this blog, or a related blog, noted that the main difference between SALV and CL_GUI_ALV_GRID was that the field catalogue was generated for you in the case of the SALV,

      They suggested using the CL_SALV_TABLE to generate the field catalogue from the internal table, and then using the CL_GUI_ALV_GRID directly to actually display the report.

      I have a Z interface which can be used with either CL_SALV_TABLE or CL_GUI_ALV_GRID without the calling program needing to know which of the two is being used at run time.

      In the latter case I do exactly what was suggested – I use CL_SALV_CONTROLLER_METADATA to get the field catalogue from an instance of CL_SALV_TABLE and then pass that into CL_GUI_ALV_GRID.

      I also hide the details of creating a screen and custom controller from the calling program.

      This is probably the most elegant solution of all the experiments I have been making.

      However I have been trying to make individual columns in the SALV editable (which I have got working), and indeed individual cells if I ever get that working, for the purpose of seeing if I can. SAP says it is impossible, even if every SAP customer want this, so naturally I want to prove them wrong.

      It is like the “why do you climb Mount Everest?” question. I am making individual columns editable in the SALV because I can!

      Cheersy Cheers


  • I have spent ages trying to get the SAPLINK file working, but the main object – my Z class for the SALV_VIEW keeps failing.

    When I import the nugget in the target system I get the standard SAPLINK message “Your pants are on fire”. Now I am hardly in a position to complain about programmers making jokes in their programs but this does violate one of “Uncle Bobs” rules namely “don’t be cute”.

    Debugging reveals that the class that throws the exception knows what the actual error message is – the SYST structure is filled with the MSGID, MSGNO and the MSGV variables. However instead of sending this information to the end user (me in this case) the real error information is discarded and a joke sent instead.

    I am not against jokes, but in this case why not send the joke AND the actual error message.

    More debugging (a lot) has revealed that the problem is that in my Z class I have an event which comes from an implemented interface. The SAPLINK code is clever enough to handle interface methods with parameters, making sure the generation does not try to recreate them. However the same thing does not happen with the interface event. Thus the ZSAPLINK tries to create the parameter for the interface, when it does not need to, and an error is generated saying the parameter already exists.

    It has taken me ages to get to this point, but now at last I know what the problem is I can go about fixing it, and then try to get my fix into the “elephants trunk” on the “snivelling little git” site where the project is hosted, so other people can benefit. I think I have got those technical terms correct.

    Cheersy Cheers


    • I have fixed my local copy of SAPLINK.

      It was a bit of a hack, but in method CREATEOBJECTFROMXMLDOC of class ZSAPLINK_CLASS I loop through the events table where ALIAS is ABAP_TRUE and delete from table CH_PARAMETERS any lines where the component name equals the events component name, but the class does not equal the events class.

      I imagine there is a more elegant way to do this, but another of those programming rules goes like “first make it work – then make it good”. Sadly in real life most people only bother with the first, due to pressure of time, and then regret it later on, thus costing more time than if they had made it good in the first place after having got it to work. In other words – more haste, less speed, at least over the program life cycle.

      However, most people reading this blog won’t want to be messing with SAPSCRIPT code, so I have to change my Z view class, just so it can be transported via SAPLINK.

      So when I upload the nugget, and you transport it into your system lots of things won’t compile.

      That is because to get the class in a state where SAPLINK works I had to take the event out of the interface, and put it directly in the ZCL_VIEW class. This stuffs up other classes relying on the event being in the interface.

      However at least everything will make it to the target system.

      Once it is there, copy the event USER_COMMAND_RECEIVED to ZIF_BC_ALV_REPORT_VIEW from ZCL_BC_VIEW_SALV_TABLE making sure you copy the parameters as well. Then delete the event from the ZCL_BC_VIEW class so that it is not there twice.

      Then change ZIF_BC_CONTROLLER so that the ON_USER_COMMAND is an event handler for the ZIF_BC… interface rather than the ZCL_BC_VIEW… class.

      That is it. I wish nobody would have to make any changes, but there you go.

      I will now try and upload the SAPLINK nugget to this blog.

      Cheersy Cheers


      • Hi Paul, I’m giving this demo a try.  Where can I find ZCL_BC_SCREEN_MESSAGE?  I don’t see it created in the nugget.  Is there a class I should copy?  Thank you.

        • That was probably the first class I ever wrote, when I did not know anything about OO and was stabbing in the dark, making all sorts of mistakes. It only has the one method, and that is static. I am sure if I did the same thing today I would do it totally differently. Anyway, here is a picture of the code of that method:-


  • Thank you both for the info!

    Paul, I have run into another issue, I followed the editing instructions above after importing the nugget, must not have done it correctly, as I have errors…

    1. Created event USER_COMMAND_RECEIVED in interface ZIF_BC_ALV_REPORT_VIEW.  I created the parameters from ZCL_BC_VIEW_SALV_TABLE (ED_USER_COMMAND, ED_ROW, ED_COLUMN).



    I am unable to activate ZCL_BC_VIEW_SALV_TABLE, methods HANDLE_LINK_CLICK and HANDLE_USER_COMMAND have syntax error there is no event USER_COMMAND_RECEIVED?  Please let me know what I need to do differently?

    Thank you.

    EDIT: Fixed typo on interface name in step #3.

    • Dear Sue,

      In ZCL_BC_VIEW_SALV_TABLE go to the “aliases” tab, and see if you can find the event right down the bottom.  Maybe the problem is because my code raises the event using an alias name?

      Alias Smth and Jones.GIF

  • Thank you Paul.

    All objects are activated now, except for Z_MONSTER_ADL_IO1 and Z_MONSTER_AM_IO1.

    Z_MONSTER_ADL_IO1 gives syntax error ED_USER_COMMAND is unknown, it’s on this statement in METHOD on_user_command:

             id_user_command = ed_user_command   ” Function code that PAI triggered
             id_column = ed_column   ” Selected Column
             id_row = ed_row )” Selected Row

    Same message from Z_MONSTER_AM_IO1.

    Thank you again for your help.

    EDIT: Changed the above to syntax error occurs in two includes, not just the one include.

    • I am gathering the error is in method ON_USER_COMMAND of class LCL_CONTROLLER in both cases.

      I am happy in regard to your correction, it puzzled me only one program would have the error.

      That method is defined as an alias of ZIF_BC_CONTROLLER~ON_USER_COMMAND.

      If I have a look at the parameters for that method definition I see the following:-


      If the ED_USER_COMMAND is missing try pressing the square box with a smaller blue box in the middle, which copies over the parameters from the parent.

      As this is an event handler for ZIF_BC_ALV_REPORT_VIEW~USER_COMMAND_RECEIVED have a look at that vent to make sure the parameters are all there.


      Just to recap – the view raises the event when the user does something, I don’t like the view knowing about the model directly, so the controller reacts to the event raised by the view, reacts by doing anything it considers it’s job, like switching views for example, it does not have to do anything, and then passes the command on to the model which may react or may ignore it.

      As always, please come back if none of this is helpful….

      Cheersy Cheers


      • Thank you Paul!  All objects compile now.  I also added the selection text for P_CSTL in program Z_MONSTER_ATROCITY_MONITOR, and both programs had extra selection texts removed.  (Mentioning only in case you update the nugget in future.)

        Now to populate some data in the tables and try the programs.  If you have any initial table data to share it is welcomed 😎

  • After importing original nugget and making the corrections, I exported new nugget then deleted these three lines between <events> and </events> in the nugget file:


       <parameter CLSNAME=”ZIF_BC_ALV_REPORT_VIEW” …  <- delete

       <parameter CLSNAME=”ZIF_BC_ALV_REPORT_VIEW”…   <- delete

       <parameter CLSNAME=”ZIF_BC_ALV_REPORT_VIEW”…   <- delete


    The edited nugget imported successfully to fresh system without previous copy of this code.  (ZCL_BC_SALV_EVENT_HANDLER and ZCL_SALV_MODEL had to be activated together due to dependencies.)  I am using latest SAPLINK from Assembla.

    In case of any interest…

  • Hello Paul,
    your example about the editable column in SALV class is really interesting.

    In below example you create the object MO_SALV_MODEL; could you please tell my how MO_SALV_MODEL is declared? I didn’t find it in the overall text.

    Thanks in advance.
    Luigi Barbieri

    “ Object to access underlying CL_GUI_ALV_GRID
    CREATE OBJECT mo_salv_model EXPORTING io_model = lo_salv_model.

    • In the INITIALISE method of ZCL_BC_VIEW_SALV_TABLE we have the following:-

      Naturally the MO_SALV_MODEL is a member attribute of the Z view class. I have used 7.40+ syntax in the above screen shot.

      Cheersy Cheers


    • When this blog was in the SCN then there was a SAPLINK nugget attached with the code. It looks like such attachments did not make it to the new improved SAP community.

      That does not make me fall off my chair with shock. Look for the SAP Press landing site for my book “ABAP To The Future” and there will be SAPLINK nuggets there for the code. You don’t even have to buy the book.

  • Hello Paul,

    Let me start by thank you for all the work you do to help the SAP developers to go forward. I have read the ABAP to the Future and it was a trully remarkable experience.

    I am having problems to download this code via SAPLINK. I went to SAP Press, download the file but for the specific nugget ZMONSTER_09_SALV_FRAMEWORK I am having an error:

    •  Internal error – you need to debug to identify the problem

    I have tryed with other nuggets, like NUGG_ZMONSTER_C00_COMMON_DDIC_OBJECTS or NUGG_ZMONSTER_C04_ABAP_TEST_COCKPIT and everything worked.

    Let me add that I add to install the SAPLINK in the machine I am working now but I already did it before and like I have said, nuggets C00 and C04 have worked.

    I install all the pluggins but maybe someone missed..even if it was some issue with pluggins, the tool normally points for the missing one.

    Thanks in advance

    • First off, thanks very much for reading my book!

      Other people have noted there are some errors in the standard SAPLINK classes, which you will have to make the change yourself, now the global SAPLINK project is dead in the water.


      Try making those changes first – if that does not work, go back to the SAP PRESS site – I had them upload a new version of the ALV nugget on the 10/03/2017 (that is the 10th of March for anyone living outside the USA).


      I would attach that new nugget to this blog, but I am not sure you can do that on the new improved “SAP Community”

      Cheersy Cheers